#region PDFsharp - A .NET library for processing PDF
//
// Authors:
//   Stefan Lange
//
// Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany)
//
// http://www.pdfsharp.com
// http://sourceforge.net/projects/pdfsharp
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
#endregion

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
#if GDI
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using GdiPoint = System.Drawing.Point;
using GdiSize = System.Drawing.Size;
using GdiRect = System.Drawing.Rectangle;
using GdiPointF = System.Drawing.PointF;
using GdiSizeF = System.Drawing.SizeF;
using GdiRectF = System.Drawing.RectangleF;
using GdiMatrix = System.Drawing.Drawing2D.Matrix;
#endif
#if WPF
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using PdfSharp.Windows;
using SysPoint = System.Windows.Point;
using SysSize = System.Windows.Size;
using SysRect = System.Windows.Rect;
using SysMatrix = System.Windows.Media.Matrix;
using WpfBrush = System.Windows.Media.Brush;
using WpfPen = System.Windows.Media.Pen;
#if !SILVERLIGHT
using WpfBrushes = System.Windows.Media.Brushes;
#endif
#endif
#if NETFX_CORE
using Windows.UI.Xaml.Media;
using SysPoint = Windows.Foundation.Point;
using SysSize = Windows.Foundation.Size;
using SysRect = Windows.Foundation.Rect;
#endif
#if UWP
using System.Numerics;
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Geometry;
using SysPoint = Windows.Foundation.Point;
using SysSize = Windows.Foundation.Size;
using SysRect = Windows.Foundation.Rect;
#endif
using PdfSharp.Pdf;
using PdfSharp.Drawing.Pdf;
using PdfSharp.Internal;
using PdfSharp.Pdf.Advanced;

#pragma warning disable 1587
// ReSharper disable UseNullPropagation
// ReSharper disable RedundantNameQualifier
// ReSharper disable UseNameofExpression

namespace PdfSharp.Drawing  // #??? Clean up
{
    /// <summary>
    /// Holds information about the current state of the XGraphics object.
    /// </summary>
    [Flags]
    enum InternalGraphicsMode
    {
        DrawingGdiGraphics,
        DrawingPdfContent,
        DrawingBitmap,
    }

    /// <summary>
    /// Represents a drawing surface for a fixed size page.
    /// </summary>
    public sealed class XGraphics : IDisposable
    {
#if CORE
        // TODO: Implement better concept of a measure context.
#endif

#if GDI
        /// <summary>
        /// Initializes a new instance of the XGraphics class.
        /// </summary>
        /// <param name="gfx">The gfx.</param>
        /// <param name="size">The size.</param>
        /// <param name="pageUnit">The page unit.</param>
        /// <param name="pageDirection">The page direction.</param>
        XGraphics(Graphics gfx, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
            if (gfx == null)
            {
                // MigraDoc comes here when creating a MeasureContext.
                try
                {
                    Lock.EnterGdiPlus();
                    gfx = Graphics.FromHwnd(IntPtr.Zero);  // BUG: Use measure image
                }
                finally { Lock.ExitGdiPlus(); }
            }

            _gsStack = new GraphicsStateStack(this);
            TargetContext = XGraphicTargetContext.GDI;
            _gfx = gfx;
            _drawGraphics = true;
            _pageSize = new XSize(size.Width, size.Height);
            _pageUnit = pageUnit;
            switch (pageUnit)
            {
                case XGraphicsUnit.Point:
                    _pageSizePoints = new XSize(size.Width, size.Height);
                    break;

                case XGraphicsUnit.Inch:
                    _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height));
                    break;

                case XGraphicsUnit.Millimeter:
                    _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height));
                    break;

                case XGraphicsUnit.Centimeter:
                    _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height));
                    break;

                case XGraphicsUnit.Presentation:
                    _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height));
                    break;

                default:
                    throw new NotImplementedException("unit");
            }

            _pageDirection = pageDirection;
            Initialize();
        }
#endif

#if WPF && !SILVERLIGHT
        /// <summary>
        /// Initializes a new instance of the XGraphics class.
        /// </summary>
        /// <param name="dc">The drawing context.</param>
        /// <param name="size">The size.</param>
        /// <param name="pageUnit">The page unit.</param>
        /// <param name="pageDirection">The page direction.</param>
        XGraphics(DrawingContext dc, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
            if (dc == null)
            {
                //throw new ArgumentNullException("dc");
                _dv = new DrawingVisual();
                dc = _dv.RenderOpen();
            }

            _gsStack = new GraphicsStateStack(this);
            TargetContext = XGraphicTargetContext.WPF;
            _dc = dc;
            _drawGraphics = true;
            _pageSize = new XSize(size.Width, size.Height);
            _pageUnit = pageUnit;
            switch (pageUnit)
            {
                case XGraphicsUnit.Point:
                    _pageSizePoints = new XSize(size.Width, size.Height);
                    break;

                case XGraphicsUnit.Inch:
                    _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height));
                    break;

                case XGraphicsUnit.Millimeter:
                    _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height));
                    break;

                case XGraphicsUnit.Centimeter:
                    _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height));
                    break;

                case XGraphicsUnit.Presentation:
                    _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height));
                    break;

                default:
                    throw new NotImplementedException("unit");
            }

            _pageDirection = pageDirection;
            Initialize();
        }
#endif

#if WPF
        /// <summary>
        /// Initializes a new instance of the XGraphics class.
        /// </summary>
        /// <param name="canvas">The canvas.</param>
        /// <param name="size">The size.</param>
        /// <param name="pageUnit">The page unit.</param>
        /// <param name="pageDirection">The page direction.</param>
        XGraphics(Canvas canvas, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
            //throw new ArgumentNullException("canvas");
            if (canvas == null)
                canvas = new Canvas();

#if !SILVERLIGHT
            // Create DrawingVisual as container for the content of the page.
            _dv = new DrawingVisual();
            // Create a host that shows the visual.
            VisualPresenter vp = new VisualPresenter();
            vp.Children.Add(_dv);
            // The canvas only contains the host of the DrawingVisual.
            canvas.Children.Add(vp);
            _dc = _dv.RenderOpen();
            TargetContext = XGraphicTargetContext.WPF;
            //////VisualBrush brush = new VisualBrush(_dv);
            ////////brush.ViewboxUnits = BrushMappingMode.
            //////brush.Viewport=new Rect(new Point(), size.ToSize());
            //////brush.Viewbox=new Rect(new Point(), size.ToSize());
            ////////brush.Viewport=new Rect(new Point(), (Size)size);
            //////brush.AutoLayoutContent = true;
            //////canvas.Background = brush;
#else
            _dc = new AgDrawingContext(canvas);
#endif

            _gsStack = new GraphicsStateStack(this);
            TargetContext = XGraphicTargetContext.WPF;

            _drawGraphics = true;
            _pageSize = new XSize(size.Width, size.Height);
            _pageUnit = pageUnit;
            switch (pageUnit)
            {
                case XGraphicsUnit.Point:
                    _pageSizePoints = new XSize(size.Width, size.Height);
                    break;

                case XGraphicsUnit.Inch:
                    _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height));
                    break;

                case XGraphicsUnit.Millimeter:
                    _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height));
                    break;

                case XGraphicsUnit.Centimeter:
                    _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height));
                    break;

                case XGraphicsUnit.Presentation:
                    _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height));
                    break;

                default:
                    throw new NotImplementedException("unit");
            }

            _pageDirection = pageDirection;
            Initialize();
        }
#endif

#if UWP
        /// <summary>
        /// Initializes a new instance of the XGraphics class.
        /// </summary>
        /// <param name="canvasDrawingSession">The canvas.</param>
        /// <param name="size">The size.</param>
        /// <param name="pageUnit">The page unit.</param>
        /// <param name="pageDirection">The page direction.</param>
        XGraphics(CanvasDrawingSession canvasDrawingSession, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
            if (canvasDrawingSession == null)
                throw new ArgumentNullException("canvasDrawingSession");

            _cds = canvasDrawingSession;

            _gsStack = new GraphicsStateStack(this);
            TargetContext = XGraphicTargetContext.WPF;

            _drawGraphics = true;
            _pageSize = new XSize(size.Width, size.Height);
            _pageUnit = pageUnit;
            switch (pageUnit)
            {
                case XGraphicsUnit.Point:
                    _pageSizePoints = new XSize(size.Width, size.Height);
                    break;

                case XGraphicsUnit.Inch:
                    _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height));
                    break;

                case XGraphicsUnit.Millimeter:
                    _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height));
                    break;

                case XGraphicsUnit.Centimeter:
                    _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height));
                    break;

                case XGraphicsUnit.Presentation:
                    _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height));
                    break;

                default:
                    throw new NotImplementedException("unit");
            }

            _pageDirection = pageDirection;
            Initialize();
        }
#endif

        /// <summary>
        /// Initializes a new instance of the XGraphics class for drawing on a PDF page.
        /// </summary>
        XGraphics(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
            if (page == null)
                throw new ArgumentNullException("page");

            if (page.Owner == null)
                throw new ArgumentException("You cannot draw on a page that is not owned by a PdfDocument object.", "page");

            if (page.RenderContent != null)
                throw new InvalidOperationException("An XGraphics object already exists for this page and must be disposed before a new one can be created.");

            if (page.Owner.IsReadOnly)
                throw new InvalidOperationException("Cannot create XGraphics for a page of a document that cannot be modified. Use PdfDocumentOpenMode.Modify.");

            _gsStack = new GraphicsStateStack(this);
            PdfContent content = null;
            switch (options)
            {
                case XGraphicsPdfPageOptions.Replace:
                    page.Contents.Elements.Clear();
                    goto case XGraphicsPdfPageOptions.Append;

                case XGraphicsPdfPageOptions.Prepend:
                    content = page.Contents.PrependContent();
                    break;

                case XGraphicsPdfPageOptions.Append:
                    content = page.Contents.AppendContent();
                    break;
            }
            page.RenderContent = content;

#if CORE
            TargetContext = XGraphicTargetContext.CORE;
#endif
#if GDI
            // HACK: This does not work with #MediumTrust
            //_gfx = Graphics.FromHwnd(IntPtr.Zero);  // _gfx should not be necessary anymore.
            _gfx = null;
            TargetContext = XGraphicTargetContext.GDI;
#endif
#if WPF && !SILVERLIGHT
            _dv = new DrawingVisual();
            _dc = _dv.RenderOpen();
            TargetContext = XGraphicTargetContext.WPF;
#endif
#if SILVERLIGHT
            _dc = new AgDrawingContext(new Canvas());
            TargetContext = XGraphicTargetContext.WPF;
#endif
#if GDI && WPF
            TargetContext = PdfSharp.Internal.TargetContextHelper.TargetContext;
#endif
            _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(page, this, options);
            _pageSizePoints = new XSize(page.Width, page.Height);
            switch (pageUnit)
            {
                case XGraphicsUnit.Point:
                    _pageSize = new XSize(page.Width, page.Height);
                    break;

                case XGraphicsUnit.Inch:
                    _pageSize = new XSize(XUnit.FromPoint(page.Width).Inch, XUnit.FromPoint(page.Height).Inch);
                    break;

                case XGraphicsUnit.Millimeter:
                    _pageSize = new XSize(XUnit.FromPoint(page.Width).Millimeter, XUnit.FromPoint(page.Height).Millimeter);
                    break;

                case XGraphicsUnit.Centimeter:
                    _pageSize = new XSize(XUnit.FromPoint(page.Width).Centimeter, XUnit.FromPoint(page.Height).Centimeter);
                    break;

                case XGraphicsUnit.Presentation:
                    _pageSize = new XSize(XUnit.FromPoint(page.Width).Presentation, XUnit.FromPoint(page.Height).Presentation);
                    break;

                default:
                    throw new NotImplementedException("unit");
            }
            _pageUnit = pageUnit;
            _pageDirection = pageDirection;

            Initialize();
        }

        /// <summary>
        /// Initializes a new instance of the XGraphics class used for drawing on a form.
        /// </summary>
        XGraphics(XForm form)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            _form = form;
            form.AssociateGraphics(this);

            _gsStack = new GraphicsStateStack(this);
#if CORE
            TargetContext = XGraphicTargetContext.CORE;
            _drawGraphics = false;
            if (form.Owner != null)
                _renderer = new XGraphicsPdfRenderer(form, this);
            _pageSize = form.Size;
            Initialize();
#endif
#if GDI && !WPF
            try
            {
                Lock.EnterGdiPlus();
                TargetContext = XGraphicTargetContext.GDI;
                // If form.Owner is null create a meta file.
                if (form.Owner == null)
                {
                    MemoryStream stream = new MemoryStream();
                    // BUG: This Windows 1.0 technique issued an exception under Microsoft Azure.  // #???
                    using (Graphics refgfx = Graphics.FromHwnd(IntPtr.Zero))
                    {
                        IntPtr hdc = refgfx.GetHdc();
#if true_
                        // This code comes from my C++ RenderContext and checks some confusing details in connection 
                        // with metafiles.
                        //                                                                                    Display                 | LaserJet
                        //                                                                               DPI   96 : 120               | 300
                        // physical device size in MM                                                    ---------------------------------------------
                        int horzSizeMM = NativeMethods.GetDeviceCaps(hdc, NativeMethods.HORZSIZE);    // = 360 : 360               | 198 (not 210)
                        int vertSizeMM = NativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTSIZE);    // = 290 : 290               | 288 (hot 297)
                                                                                                      // Cool:
                                                                                                      // My monitor is a Sony SDM-N80 and its size is EXACTLY 360mm x 290mm!!
                                                                                                      // It is an 18.1" Flat Panel LCD from 2002 and these are the values
                                                                                                      // an older display drivers reports in about 2003:
                                                                                                      //        Display  
                                                                                                      //  DPI   96 : 120
                                                                                                      //  --------------
                                                                                                      //       330 : 254
                                                                                                      //       254 : 203
                                                                                                      // Obviously my ATI driver reports the exact size of the monitor.


                        // device size in pixel
                        int horzSizePixel = NativeMethods.GetDeviceCaps(hdc, NativeMethods.HORZRES);     // = 1280 : 1280             | 4676
                        int vertSizePixel = NativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTRES);     // = 1024 : 1024             | 6814

                        // 'logical' device resolution in DPI
                        int logResX = NativeMethods.GetDeviceCaps(hdc, NativeMethods.LOGPIXELSX);  // = 96 : 120                | 600
                        int logResY = NativeMethods.GetDeviceCaps(hdc, NativeMethods.LOGPIXELSY);  // = 96 : 120                | 600

                        // now we can get the 'physical' device resolution...
                        float phyResX = horzSizePixel / (horzSizeMM / 25.4f);  // = 98.521210 : 128.00000   | 599.85052
                        float phyResY = vertSizePixel / (vertSizeMM / 25.4f);  // = 102.40000 : 128.12611   | 600.95691

                        // ...and rescale the size of the meta rectangle.
                        float magicX = logResX / phyResX;                      // = 0.97440946 : 0.93750000 | 1.0002491
                        float magicY = logResY / phyResY;                      // = 0.93750000 : 0.93657720 | 0.99840766

                        // use A4 page in point
                        // adjust size of A4 page so that meta file fits with DrawImage...
                        //GdiRectF rcMagic = new GdiRectF(0, 0, magicX * form.Width, magicY * form.Height);
                        //m_PreviewMetafile = new Metafile(hdc, rcMagic, MetafileFrameUnitPoint,
                        //  EmfTypeEmfPlusOnly, L"some description");
#endif
                        GdiRectF rect = new GdiRectF(0, 0, form.PixelWidth, form.PixelHeight);
                        Metafile = new Metafile(stream, hdc, rect, MetafileFrameUnit.Pixel); //, EmfType.EmfPlusOnly);

                        // Petzold disposes the refgfx object, although the hdc is in use of the metafile
                        refgfx.ReleaseHdc(hdc);
                    } // refgfx.Dispose();

                    _gfx = Graphics.FromImage(Metafile);
                    _gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
                    _drawGraphics = true;
                }
                else
                {
                    Metafile = null;
                    _gfx = Graphics.FromHwnd(IntPtr.Zero);
                }
                if (form.Owner != null)
                    _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(form, this);
                _pageSize = form.Size;
            }
            finally { Lock.ExitGdiPlus(); }
            Initialize();
#endif
#if WPF && !GDI
            TargetContext = XGraphicTargetContext.WPF;
#if !SILVERLIGHT
            // If form.Owner is null create a meta file.
            if (form.Owner == null)
            {
                _dv = new DrawingVisual();
                _dc = _dv.RenderOpen();
                _drawGraphics = true;
            }
            else
            {
                _dv = new DrawingVisual();
                _dc = _dv.RenderOpen();
            }
            if (form.Owner != null)
                _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(form, this);
            _pageSize = form.Size;
            Initialize();
#else
            throw new NotImplementedException(); // AGHACK
            //Initialize();
#endif
#endif
        }

        /// <summary>
        /// Creates the measure context. This is a graphics context created only for querying measures of text.
        /// Drawing on a measure context has no effect.
        /// </summary>
        public static XGraphics CreateMeasureContext(XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection)
        {
#if CORE
            //throw new InvalidOperationException("No measure context in CORE build.");
            PdfDocument dummy = new PdfDocument();
            PdfPage page = dummy.AddPage();
            //XGraphics gfx = new XGraphics(((System.Drawing.Graphics)null, size, pageUnit, pageDirection);
            XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append, pageUnit, pageDirection);
            return gfx;
#endif
#if GDI && !WPF
            //XGraphics gfx = new XGraphics((System.Drawing.Graphics)null, size, pageUnit, pageDirection);
            XGraphics gfx = new XGraphics((System.Drawing.Graphics)null, size, pageUnit, pageDirection);
            return gfx;
#endif
#if WPF && !SILVERLIGHT
            XGraphics gfx = new XGraphics((System.Windows.Media.DrawingContext)null, size, pageUnit, pageDirection);
            return gfx;
#endif
#if SILVERLIGHT
            XGraphics gfx = new XGraphics(new Canvas(), size, pageUnit, pageDirection);
            return gfx;
#endif
#if NETFX_CORE || UWP // NETFX_CORE_TODO
            return null;
#endif
        }

#if GDI
        /// <summary>
        /// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object.
        /// </summary>
        public static XGraphics FromGraphics(Graphics graphics, XSize size)
        {
            // Creating a new instance is by design.
            return new XGraphics(graphics, size, XGraphicsUnit.Point, XPageDirection.Downwards);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object.
        /// </summary>
        public static XGraphics FromGraphics(Graphics graphics, XSize size, XGraphicsUnit unit)
        {
            // Creating a new instance is by design.
            return new XGraphics(graphics, size, unit, XPageDirection.Downwards);
        }

        ///// <summary>
        ///// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object.
        ///// </summary>
        //public static XGraphics FromGraphics(Graphics graphics, XSize size, XPageDirection pageDirection)
        //{
        //  // Creating a new instance is by design.
        //  return new XGraphics(graphics, size, XGraphicsUnit.Point, pageDirection);
        //}

        ///// <summary>
        ///// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object.
        ///// </summary>
        //public static XGraphics FromGraphics(Graphics graphics, XSize size, XGraphicsUnit unit, XPageDirection pageDirection)
        //{
        //  // Creating a new instance is by design.
        //  return new XGraphics(graphics, size, XGraphicsUnit.Point, pageDirection);
        //}
#endif

#if WPF && !SILVERLIGHT
        /// <summary>
        /// Creates a new instance of the XGraphics class from a System.Windows.Media.DrawingContext object.
        /// </summary>
        public static XGraphics FromDrawingContext(DrawingContext drawingContext, XSize size, XGraphicsUnit unit)
        {
            return new XGraphics(drawingContext, size, unit, XPageDirection.Downwards);
        }
#endif

#if WPF
        /// <summary>
        /// Creates a new instance of the XGraphics class from a System.Windows.Media.DrawingContext object.
        /// </summary>
        public static XGraphics FromCanvas(Canvas canvas, XSize size, XGraphicsUnit unit)
        {
            return new XGraphics(canvas, size, unit, XPageDirection.Downwards);
        }
#endif

#if UWP
        /// <summary>
        /// Creates a new instance of the XGraphics class from a  Microsoft.Graphics.Canvas.CanvasDrawingSession object.
        /// </summary>
        public static XGraphics FromCanvasDrawingSession(CanvasDrawingSession drawingSession, XSize size, XGraphicsUnit unit)
        {
            return new XGraphics(drawingSession, size, unit, XPageDirection.Downwards);
        }
#endif

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page)
        {
            return new XGraphics(page, XGraphicsPdfPageOptions.Append, XGraphicsUnit.Point, XPageDirection.Downwards);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XGraphicsUnit unit)
        {
            return new XGraphics(page, XGraphicsPdfPageOptions.Append, unit, XPageDirection.Downwards);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XPageDirection pageDirection)
        {
            return new XGraphics(page, XGraphicsPdfPageOptions.Append, XGraphicsUnit.Point, pageDirection);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options)
        {
            return new XGraphics(page, options, XGraphicsUnit.Point, XPageDirection.Downwards);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XPageDirection pageDirection)
        {
            return new XGraphics(page, options, XGraphicsUnit.Point, pageDirection);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit unit)
        {
            return new XGraphics(page, options, unit, XPageDirection.Downwards);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object.
        /// </summary>
        public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit unit, XPageDirection pageDirection)
        {
            return new XGraphics(page, options, unit, pageDirection);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XPdfForm object.
        /// </summary>
        public static XGraphics FromPdfForm(XPdfForm form)
        {
            if (form.Gfx != null)
                return form.Gfx;

            return new XGraphics(form);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XForm object.
        /// </summary>
        public static XGraphics FromForm(XForm form)
        {
            if (form.Gfx != null)
                return form.Gfx;

            return new XGraphics(form);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XForm object.
        /// </summary>
        public static XGraphics FromImage(XImage image)
        {
            return FromImage(image, XGraphicsUnit.Point);
        }

        /// <summary>
        /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XImage object.
        /// </summary>
        public static XGraphics FromImage(XImage image, XGraphicsUnit unit)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            XBitmapImage bmImage = image as XBitmapImage;
            if (bmImage != null)
            {
#if CORE
                return null;
#endif
#if GDI && !WPF
                Graphics gfx = Graphics.FromImage(image._gdiImage);
                image.XImageState = image.XImageState | XImageState.UsedInDrawingContext;
                return new XGraphics(gfx, new XSize(image.PixelWidth, image.PixelHeight), unit, XPageDirection.Downwards);
#endif
#if WPF && !GDI
                DiagnosticsHelper.ThrowNotImplementedException("WPF image");
                return null;
#endif
#if NETFX_CORE
                DiagnosticsHelper.ThrowNotImplementedException("NETFX_CORE image");
                return null;
#endif
            }
            return null;
        }

        /// <summary>
        /// Internal setup.
        /// </summary>
        void Initialize()
        {
            _pageOrigin = new XPoint();

            double pageHeight = _pageSize.Height;
            PdfPage targetPage = PdfPage;
            XPoint trimOffset = new XPoint();
            if (targetPage != null && targetPage.TrimMargins.AreSet)
            {
                pageHeight += targetPage.TrimMargins.Top.Point + targetPage.TrimMargins.Bottom.Point;
                trimOffset = new XPoint(targetPage.TrimMargins.Left.Point, targetPage.TrimMargins.Top.Point);
            }

            XMatrix matrix = new XMatrix();
#if CORE
            // Nothing to do here.
            Debug.Assert(TargetContext == XGraphicTargetContext.CORE);
#endif
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                try
                {
                    Lock.EnterFontFactory();
                    if (_gfx != null)
                        matrix = _gfx.Transform;

                    if (_pageUnit != XGraphicsUnit.Point)
                    {
                        switch (_pageUnit)
                        {
                            case XGraphicsUnit.Inch:
                                matrix.ScalePrepend(XUnit.InchFactor);
                                break;

                            case XGraphicsUnit.Millimeter:
                                matrix.ScalePrepend(XUnit.MillimeterFactor);
                                break;

                            case XGraphicsUnit.Centimeter:
                                matrix.ScalePrepend(XUnit.CentimeterFactor);
                                break;

                            case XGraphicsUnit.Presentation:
                                matrix.ScalePrepend(XUnit.PresentationFactor);
                                break;
                        }
                        if (_gfx != null)
                            _gfx.Transform = (GdiMatrix)matrix;
                    }
                }
                finally { Lock.ExitFontFactory(); }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
            {
                if (_pageUnit != XGraphicsUnit.Presentation)
                {
                    switch (_pageUnit)
                    {
                        case XGraphicsUnit.Point:
                            matrix.ScalePrepend(XUnit.PointFactorWpf);
                            break;

                        case XGraphicsUnit.Inch:
                            matrix.ScalePrepend(XUnit.InchFactorWpf);
                            break;

                        case XGraphicsUnit.Millimeter:
                            matrix.ScalePrepend(XUnit.MillimeterFactorWpf);
                            break;

                        case XGraphicsUnit.Centimeter:
                            matrix.ScalePrepend(XUnit.CentimeterFactorWpf);
                            break;
                    }
                    if (!matrix.IsIdentity)
                    {
#if !SILVERLIGHT
                        MatrixTransform transform = new MatrixTransform((SysMatrix)matrix);
                        _dc.PushTransform(transform);
#else
                        MatrixTransform transform2 = new MatrixTransform();
                        transform2.Matrix = (SysMatrix)matrix;
                        _dc.PushTransform(transform2);
#endif
                    }
                }
            }
#endif
            if (_pageDirection != XPageDirection.Downwards)
                matrix.Prepend(new XMatrix(1, 0, 0, -1, 0, pageHeight));

            if (trimOffset != new XPoint())
                matrix.TranslatePrepend(trimOffset.X, -trimOffset.Y);

            DefaultViewMatrix = matrix;
            _transform = new XMatrix();
        }

        /// <summary>
        /// Releases all resources used by this object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                _disposed = true;
                if (disposing)
                {
                    // Dispose managed resources.
                    if (_associatedImage != null)
                    {
                        _associatedImage.DisassociateWithGraphics(this);
                        _associatedImage = null;
                    }
                }

                if (_form != null)
                    _form.Finish();
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        // GDI+ requires this to disassociate it from metafiles.
                        if (_gfx != null)
                            _gfx.Dispose();
                        _gfx = null;
                        Metafile = null;
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (_dc != null)
                {
                    _dc.Close();
#if !SILVERLIGHT
                    // Free resources. Only needed when running on a server, but does no harm with desktop applications.
                    // Needed on server, but causes harm with WPF desktop application. So now what?
                    //_dc.Dispatcher.InvokeShutdown();

                    _dv = null;
#endif
                }
#endif
                _drawGraphics = false;

                if (_renderer != null)
                {
                    _renderer.Close();
                    _renderer = null;
                }
            }
        }
        bool _disposed;

        /// <summary>
        /// Internal hack for MigraDoc. Will be removed in further releases.
        /// Unicode support requires a global refactoring of MigraDoc and will be done in further releases.
        /// </summary>
        // ReSharper disable once InconsistentNaming
        // ReSharper disable once ConvertToAutoProperty
        public PdfFontEncoding MUH  // MigraDoc Unicode Hack...
        {
            get { return _muh; }
            set { _muh = value; }
        }
        PdfFontEncoding _muh;

        /// <summary>
        /// A value indicating whether GDI+ or WPF is used as context.
        /// </summary>
        internal XGraphicTargetContext TargetContext;

        /// <summary>
        /// Gets or sets the unit of measure used for page coordinates.
        /// CURRENTLY ONLY POINT IS IMPLEMENTED.
        /// </summary>
        public XGraphicsUnit PageUnit
        {
            get { return _pageUnit; }
            //set
            //{
            //  // TODO: other page units
            //  if (value != XGraphicsUnit.Point)
            //    throw new NotImplementedException("PageUnit must be XGraphicsUnit.Point in current implementation.");
            //}
        }
        readonly XGraphicsUnit _pageUnit;

        /// <summary>
        /// Gets or sets the a value indicating in which direction y-value grow.
        /// </summary>
        public XPageDirection PageDirection
        {
            get { return _pageDirection; }
            set
            {
                // Is there really anybody who needs the concept of XPageDirection.Upwards?
                if (value != XPageDirection.Downwards)
                    throw new NotImplementedException("PageDirection must be XPageDirection.Downwards in current implementation.");
            }
        }
        readonly XPageDirection _pageDirection;

        /// <summary>
        /// Gets the current page origin. Setting the origin is not yet implemented.
        /// </summary>
        public XPoint PageOrigin
        {
            get { return _pageOrigin; }
            set
            {
                // Is there really anybody who needs to set the page origin?
                if (value != new XPoint())
                    throw new NotImplementedException("PageOrigin cannot be modified in current implementation.");
            }
        }
        XPoint _pageOrigin;

        /// <summary>
        /// Gets the current size of the page.
        /// </summary>
        public XSize PageSize
        {
            get { return _pageSize; }
            //set
            //{
            //  //TODO
            //  throw new NotImplementedException("PageSize cannot be modified in current implementation.");
            //}
        }
        XSize _pageSize;
        XSize _pageSizePoints;

        #region Drawing

        // ----- DrawLine -----------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws a line connecting two Point structures.
        /// </summary>
        public void DrawLine(XPen pen, GdiPoint pt1, GdiPoint pt2)
        {
            // Because of overloading the cast is NOT redundant.
            DrawLine(pen, (double)pt1.X, (double)pt1.Y, (double)pt2.X, (double)pt2.Y);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a line connecting two Point structures.
        /// </summary>
        public void DrawLine(XPen pen, SysPoint pt1, SysPoint pt2)
        {
            DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a line connecting two GdiPointF structures.
        /// </summary>
        public void DrawLine(XPen pen, GdiPointF pt1, GdiPointF pt2)
        {
            DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
        }
#endif

        /// <summary>
        /// Draws a line connecting two XPoint structures.
        /// </summary>
        public void DrawLine(XPen pen, XPoint pt1, XPoint pt2)
        {
            DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
        }

        /// <summary>
        /// Draws a line connecting the two points specified by coordinate pairs.
        /// </summary>
        public void DrawLine(XPen pen, double x1, double y1, double x2, double y2)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawLine(pen.RealizeGdiPen(), (float)x1, (float)y1, (float)x2, (float)y2);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawLine(pen.RealizeWpfPen(), new SysPoint(x1, y1), new SysPoint(x2, y2));
#endif
#if UWP
                _cds.DrawLine(new Vector2((float)x1, (float)x2), new Vector2((float)x2, (float)y2), Colors.Red, (float)pen.Width);
#endif
            }

            if (_renderer != null)
                _renderer.DrawLines(pen, new[] { new XPoint(x1, y1), new XPoint(x2, y2) });
        }

        // ----- DrawLines ----------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws a series of line segments that connect an array of points.
        /// </summary>
        public void DrawLines(XPen pen, GdiPoint[] points)
        {
            DrawLines(pen, MakePointFArray(points, 0, points.Length));
        }
#endif

#if WPF || NETFX_CORE
        /// <summary>
        /// Draws a series of line segments that connect an array of points.
        /// </summary>
        public void DrawLines(XPen pen, SysPoint[] points)
        {
            DrawLines(pen, XGraphics.MakeXPointArray(points, 0, points.Length));
        }
#endif

#if GDI
        /// <summary>
        /// Draws a series of line segments that connect an array of points.
        /// </summary>
        public void DrawLines(XPen pen, GdiPointF[] points)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (points == null)
                throw new ArgumentNullException("points");
            if (points.Length < 2)
                throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points");

            if (_drawGraphics)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    _gfx.DrawLines(pen.RealizeGdiPen(), points);
                }
                finally { Lock.ExitGdiPlus(); }
            }

            if (_renderer != null)
                _renderer.DrawLines(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

        /// <summary>
        /// Draws a series of line segments that connect an array of points.
        /// </summary>
        public void DrawLines(XPen pen, XPoint[] points)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (points == null)
                throw new ArgumentNullException("points");
            if (points.Length < 2)
                throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawLines(pen.RealizeGdiPen(), XGraphics.MakePointFArray(points));
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
#if !SILVERLIGHT
                    PolyLineSegment seg = new PolyLineSegment(XGraphics.MakePointArray(points), true);
#else
                    Point[] pts = XGraphics.MakePointArray(points);
                    PointCollection collection = new PointCollection();
                    foreach (Point point in pts)
                        collection.Add(point);
                    PolyLineSegment seg = new PolyLineSegment();
                    seg.Points = collection;
#endif
                    PathFigure figure = new PathFigure();
                    figure.IsFilled = false;
                    figure.StartPoint = new SysPoint(points[0].X, points[0].Y);
                    figure.Segments.Add(seg);
                    PathGeometry geo = new PathGeometry();
                    geo.Figures.Add(figure);
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo);
                }
#endif
#if UWP
                var pathBuilder = new CanvasPathBuilder(_cds.Device);
                pathBuilder.BeginFigure((float)points[0].X, (float)points[0].Y, CanvasFigureFill.DoesNotAffectFills);
                int length = points.Length;
                for (int idx = 1; idx < length; idx++)
                    pathBuilder.AddLine((float)points[idx].X, (float)points[idx].Y);
                pathBuilder.EndFigure(CanvasFigureLoop.Open);
                var geometry = CanvasGeometry.CreatePath(pathBuilder);
                _cds.DrawGeometry(geometry, Colors.Red);
#endif
            }

            if (_renderer != null)
                _renderer.DrawLines(pen, points);
        }

        /// <summary>
        /// Draws a series of line segments that connect an array of x and y pairs.
        /// </summary>
        public void DrawLines(XPen pen, double x, double y, params double[] value)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (value == null)
                throw new ArgumentNullException("value");

            int length = value.Length;
            XPoint[] points = new XPoint[length / 2 + 1];
            points[0].X = x;
            points[0].Y = y;
            for (int idx = 0; idx < length / 2; idx++)
            {
                points[idx + 1].X = value[2 * idx];
                points[idx + 1].Y = value[2 * idx + 1];
            }
            DrawLines(pen, points);
        }

        // ----- DrawBezier ---------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws a Bézier spline defined by four points.
        /// </summary>
        public void DrawBezier(XPen pen, GdiPoint pt1, GdiPoint pt2, GdiPoint pt3, GdiPoint pt4)
        {
            // ReSharper disable RedundantCast because it is required
            DrawBezier(pen, (double)pt1.X, (double)pt1.Y, (double)pt2.X, (double)pt2.Y,
              (double)pt3.X, (double)pt3.Y, (double)pt4.X, (double)pt4.Y);
            // ReSharper restore RedundantCast
        }
#endif

#if WPF
        /// <summary>
        /// Draws a Bézier spline defined by four points.
        /// </summary>
        public void DrawBezier(XPen pen, SysPoint pt1, SysPoint pt2, SysPoint pt3, SysPoint pt4)
        {
            DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a Bézier spline defined by four points.
        /// </summary>
        public void DrawBezier(XPen pen, GdiPointF pt1, GdiPointF pt2, GdiPointF pt3, GdiPointF pt4)
        {
            DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y);
        }
#endif

        /// <summary>
        /// Draws a Bézier spline defined by four points.
        /// </summary>
        public void DrawBezier(XPen pen, XPoint pt1, XPoint pt2, XPoint pt3, XPoint pt4)
        {
            DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y);
        }

        /// <summary>
        /// Draws a Bézier spline defined by four points.
        /// </summary>
        public void DrawBezier(XPen pen, double x1, double y1, double x2, double y2,
          double x3, double y3, double x4, double y4)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawBezier(pen.RealizeGdiPen(), (float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3, (float)x4, (float)y4);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
#if !SILVERLIGHT
                    BezierSegment seg = new BezierSegment(new SysPoint(x2, y2), new SysPoint(x3, y3), new SysPoint(x4, y4), true);
#else
                    BezierSegment seg = new BezierSegment();
                    seg.Point1 = new SysPoint(x2, y2);
                    seg.Point2 = new SysPoint(x3, y3);
                    seg.Point3 = new SysPoint(x4, y4);
#endif
                    PathFigure figure = new PathFigure();
                    figure.StartPoint = new SysPoint(x1, y1);
                    figure.Segments.Add(seg);
                    PathGeometry geo = new PathGeometry();
                    geo.Figures.Add(figure);
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawBeziers(pen,
                  new XPoint[] { new XPoint(x1, y1), new XPoint(x2, y2), new XPoint(x3, y3), new XPoint(x4, y4) });
        }

        // ----- DrawBeziers --------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws a series of Bézier splines from an array of points.
        /// </summary>
        public void DrawBeziers(XPen pen, GdiPoint[] points)
        {
            DrawBeziers(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

#if WPF
        /// <summary>
        /// Draws a series of Bézier splines from an array of points.
        /// </summary>
        public void DrawBeziers(XPen pen, SysPoint[] points)
        {
            DrawBeziers(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

#if GDI
        /// <summary>
        /// Draws a series of Bézier splines from an array of points.
        /// </summary>
        public void DrawBeziers(XPen pen, GdiPointF[] points)
        {
            DrawBeziers(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

        /// <summary>
        /// Draws a series of Bézier splines from an array of points.
        /// </summary>
        public void DrawBeziers(XPen pen, XPoint[] points)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            int count = points.Length;
            if (count == 0)
                return;

            if ((count - 1) % 3 != 0)
                throw new ArgumentException("Invalid number of points for bezier curves. Number must fulfill 4+3n.", "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawBeziers(pen.RealizeGdiPen(), MakePointFArray(points));
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    PathFigure figure = new PathFigure();
                    figure.StartPoint = new SysPoint(points[0].X, points[0].Y);
                    for (int idx = 1; idx < count; idx += 3)
                    {
#if !SILVERLIGHT
                        BezierSegment seg = new BezierSegment(
                            new SysPoint(points[idx].X, points[idx].Y),
                            new SysPoint(points[idx + 1].X, points[idx + 1].Y),
                            new SysPoint(points[idx + 2].X, points[idx + 2].Y), true);
#else
                        BezierSegment seg = new BezierSegment();
                        seg.Point1 = new SysPoint(points[idx].X, points[idx].Y);
                        seg.Point2 = new SysPoint(points[idx + 1].X, points[idx + 1].Y);
                        seg.Point3 = new SysPoint(points[idx + 2].X, points[idx + 2].Y);
#endif
                        figure.Segments.Add(seg);
                    }
                    PathGeometry geo = new PathGeometry();
                    geo.Figures.Add(figure);
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawBeziers(pen, points);
        }

        // ----- DrawCurve ----------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws a cardinal spline through a specified array of points.
        /// </summary>
        public void DrawCurve(XPen pen, GdiPoint[] points)
        {
            DrawCurve(pen, MakePointFArray(points, 0, points.Length), 0.5);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of point using a specified tension.
        /// The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, GdiPoint[] points, int offset, int numberOfSegments, double tension)
        {
            DrawCurve(pen, MakePointFArray(points, offset, numberOfSegments), tension);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a cardinal spline through a specified array of points.
        /// </summary>
        public void DrawCurve(XPen pen, SysPoint[] points)
        {
            DrawCurve(pen, MakeXPointArray(points, 0, points.Length), 0.5);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of point. The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, SysPoint[] points, int offset, int numberOfSegments)
        {
            DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a cardinal spline through a specified array of points.
        /// </summary>
        public void DrawCurve(XPen pen, GdiPointF[] points)
        {
            DrawCurve(pen, MakeXPointArray(points, 0, points.Length), 0.5);
        }
#endif

        /// <summary>
        /// Draws a cardinal spline through a specified array of points.
        /// </summary>
        public void DrawCurve(XPen pen, XPoint[] points)
        {
            DrawCurve(pen, points, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a cardinal spline through a specified array of points using a specified tension. 
        /// </summary>
        public void DrawCurve(XPen pen, GdiPoint[] points, double tension)
        {
            DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a cardinal spline through a specified array of points using a specified tension. 
        /// </summary>
        public void DrawCurve(XPen pen, SysPoint[] points, double tension)
        {
            DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of point using a specified tension.
        /// The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, SysPoint[] points, int offset, int numberOfSegments, double tension)
        {
            DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), tension);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a cardinal spline through a specified array of points using a specified tension. 
        /// </summary>
        public void DrawCurve(XPen pen, GdiPointF[] points, double tension)
        {
            DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of point. The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, GdiPointF[] points, int offset, int numberOfSegments)
        {
            DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), 0.5);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of point using a specified tension.
        /// The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, GdiPointF[] points, int offset, int numberOfSegments, double tension)
        {
            DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), tension);
        }
#endif

        /// <summary>
        /// Draws a cardinal spline through a specified array of point using a specified tension.
        /// The drawing begins offset from the beginning of the array.
        /// </summary>
        public void DrawCurve(XPen pen, XPoint[] points, int offset, int numberOfSegments, double tension)
        {
            XPoint[] points2 = new XPoint[numberOfSegments];
            Array.Copy(points, offset, points2, 0, numberOfSegments);
            DrawCurve(pen, points2, tension);
        }

        /// <summary>
        /// Draws a cardinal spline through a specified array of points using a specified tension. 
        /// </summary>
        public void DrawCurve(XPen pen, XPoint[] points, double tension)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (points == null)
                throw new ArgumentNullException("points");

            int count = points.Length;
            if (count < 2)
                throw new ArgumentException("DrawCurve requires two or more points.", "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawCurve(pen.RealizeGdiPen(), MakePointFArray(points), (float)tension);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    tension /= 3;

                    PathFigure figure = new PathFigure();
                    figure.StartPoint = new SysPoint(points[0].X, points[0].Y);
                    if (count == 2)
                    {
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension));
                    }
                    else
                    {
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension));
                        for (int idx = 1; idx < count - 2; idx++)
                            figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension));
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension));
                    }
                    PathGeometry geo = new PathGeometry();
                    geo.Figures.Add(figure);
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawCurve(pen, points, tension);
        }

        // ----- DrawArc ------------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws an arc representing a portion of an ellipse.
        /// </summary>
        public void DrawArc(XPen pen, Rectangle rect, double startAngle, double sweepAngle)
        {
            // Because of overloading the cast is NOT redundant.
            DrawArc(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle);
        }
#endif

#if GDI
        /// <summary>
        /// Draws an arc representing a portion of an ellipse.
        /// </summary>
        public void DrawArc(XPen pen, GdiRectF rect, double startAngle, double sweepAngle)
        {
            DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }
#endif

        /// <summary>
        /// Draws an arc representing a portion of an ellipse.
        /// </summary>
        public void DrawArc(XPen pen, XRect rect, double startAngle, double sweepAngle)
        {
            DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }

        /// <summary>
        /// Draws an arc representing a portion of an ellipse.
        /// </summary>
        public void DrawArc(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            if (Math.Abs(sweepAngle) >= 360)
            {
                DrawEllipse(pen, x, y, width, height);
            }
            else
            {
                if (_drawGraphics)
                {
#if GDI
                    if (TargetContext == XGraphicTargetContext.GDI)
                    {
                        try
                        {
                            Lock.EnterGdiPlus();
                            _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle);
                        }
                        finally { Lock.ExitGdiPlus(); }
                    }
#endif
#if WPF
                    if (TargetContext == XGraphicTargetContext.WPF)
                    {
                        SysPoint startPoint;
                        ArcSegment seg = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startPoint);
                        PathFigure figure = new PathFigure();
                        figure.StartPoint = startPoint;
                        figure.Segments.Add(seg);
                        PathGeometry geo = new PathGeometry();
                        geo.Figures.Add(figure);
                        _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo);
                    }
#endif
                }

                if (_renderer != null)
                    _renderer.DrawArc(pen, x, y, width, height, startAngle, sweepAngle);
            }
        }

        // ----- DrawRectangle ------------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, Rectangle rect)
        {
            // Because of overloading the cast is NOT redundant.
            DrawRectangle(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, GdiRectF rect)
        {
            DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, XRect rect)
        {
            DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, double x, double y, double width, double height)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawRectangle(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    _dc.DrawRectangle(null, pen.RealizeWpfPen(), new Rect(x, y, width, height));
                }
#endif
#if UWP
                if (TargetContext == XGraphicTargetContext.UWP)
                {
                    _cds.DrawRectangle((float)x, (float)y, (float)width, (float)height, pen.Color.ToUwpColor());
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawRectangle(pen, null, x, y, width, height);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XBrush brush, Rectangle rect)
        {
            // Because of overloading the cast is NOT redundant.
            DrawRectangle(brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XBrush brush, GdiRectF rect)
        {
            DrawRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XBrush brush, XRect rect)
        {
            DrawRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XBrush brush, double x, double y, double width, double height)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.FillRectangle(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawRectangle(brush.RealizeWpfBrush(), null, new Rect(x, y, width, height));
#endif
#if UWP
                if (TargetContext == XGraphicTargetContext.UWP)
                {
                    _cds.DrawRectangle((float)x, (float)y, (float)width, (float)height, brush.RealizeCanvasBrush());
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawRectangle(null, brush, x, y, width, height);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, XBrush brush, Rectangle rect)
        {
            // Because of overloading the cast is NOT redundant.
            DrawRectangle(pen, brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, XBrush brush, GdiRectF rect)
        {
            DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, XBrush brush, XRect rect)
        {
            DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        public void DrawRectangle(XPen pen, XBrush brush, double x, double y, double width, double height)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillRectangle(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height);
                        if (pen != null)
                            _gfx.DrawRectangle(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawRectangle(
                      brush != null ? brush.RealizeWpfBrush() : null,
                      pen != null ? pen.RealizeWpfPen() : null,
                      new Rect(x, y, width, height));
#endif
            }

            if (_renderer != null)
                _renderer.DrawRectangle(pen, brush, x, y, width, height);
        }

        // ----- DrawRectangles -----------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, GdiRect[] rectangles)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(pen, null, rectangles);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, GdiRectF[] rectangles)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(pen, null, rectangles);
        }
#endif

        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, XRect[] rectangles)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(pen, null, rectangles);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XBrush brush, GdiRect[] rectangles)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(null, brush, rectangles);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XBrush brush, GdiRectF[] rectangles)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(null, brush, rectangles);
        }
#endif

        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XBrush brush, XRect[] rectangles)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            DrawRectangles(null, brush, rectangles);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, XBrush brush, Rectangle[] rectangles)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            if (_drawGraphics)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    if (brush != null)
                        _gfx.FillRectangles(brush.RealizeGdiBrush(), rectangles);
                    if (pen != null)
                        _gfx.DrawRectangles(pen.RealizeGdiPen(), rectangles);
                }
                finally { Lock.ExitGdiPlus(); }
            }
            if (_renderer != null)
            {
                int count = rectangles.Length;
                for (int idx = 0; idx < count; idx++)
                {
                    Rectangle rect = rectangles[idx];
                    _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
                }
            }
        }
#endif

#if GDI
        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, XBrush brush, GdiRectF[] rectangles)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            if (_drawGraphics)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    if (brush != null)
                        _gfx.FillRectangles(brush.RealizeGdiBrush(), rectangles);
                    if (pen != null)
                        _gfx.DrawRectangles(pen.RealizeGdiPen(), rectangles);
                }
                finally { Lock.ExitGdiPlus(); }
            }
            if (_renderer != null)
            {
                int count = rectangles.Length;
                for (int idx = 0; idx < count; idx++)
                {
                    GdiRectF rect = rectangles[idx];
                    _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
                }
            }
        }
#endif

        /// <summary>
        /// Draws a series of rectangles.
        /// </summary>
        public void DrawRectangles(XPen pen, XBrush brush, XRect[] rectangles)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            if (rectangles == null)
                throw new ArgumentNullException("rectangles");

            int count = rectangles.Length;
            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    GdiRectF[] rects = MakeRectangleFArray(rectangles, 0, rectangles.Length);
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillRectangles(brush.RealizeGdiBrush(), rects);
                        if (pen != null)
                            _gfx.DrawRectangles(pen.RealizeGdiPen(), rects);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null;
                    WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null;
                    for (int idx = 0; idx < count; idx++)
                    {
                        XRect rect = rectangles[idx];
                        _dc.DrawRectangle(wpfBrush, wpfPen, new SysRect(new SysPoint(rect.X, rect.Y), new SysSize(rect.Width, rect.Height)));
                    }
                }
#endif
            }

            if (_renderer != null)
            {
                for (int idx = 0; idx < count; idx++)
                {
                    XRect rect = rectangles[idx];
                    _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
                }
            }
        }

        // ----- DrawRoundedRectangle -----------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, Rectangle rect, GdiSize ellipseSize)
        {
            DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, Rect rect, SysSize ellipseSize)
        {
            DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, GdiRectF rect, SizeF ellipseSize)
        {
            DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XRect rect, XSize ellipseSize)
        {
            DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            DrawRoundedRectangle(pen, null, x, y, width, height, ellipseWidth, ellipseHeight);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XBrush brush, Rectangle rect, GdiSize ellipseSize)
        {
            DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XBrush brush, Rect rect, SysSize ellipseSize)
        {
            DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XBrush brush, GdiRectF rect, SizeF ellipseSize)
        {
            DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XBrush brush, XRect rect, XSize ellipseSize)
        {
            DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XBrush brush, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");

            DrawRoundedRectangle(null, brush, x, y, width, height, ellipseWidth, ellipseHeight);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XBrush brush, Rectangle rect, GdiSize ellipseSize)
        {
            // ReSharper disable RedundantCast because it is required
            DrawRoundedRectangle(pen, brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height,
                (double)ellipseSize.Width, (double)ellipseSize.Height);
            // ReSharper restore RedundantCast
        }
#endif

#if WPF
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XBrush brush, Rect rect, SysSize ellipseSize)
        {
            DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XBrush brush, GdiRectF rect, SizeF ellipseSize)
        {
            DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }
#endif

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XBrush brush, XRect rect, XSize ellipseSize)
        {
            DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height);
        }

        /// <summary>
        /// Draws a rectangles with round corners.
        /// </summary>
        public void DrawRoundedRectangle(XPen pen, XBrush brush, double x, double y, double width, double height,
            double ellipseWidth, double ellipseHeight)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        XGraphicsPath path = new XGraphicsPath();
                        path.AddRoundedRectangle(x, y, width, height, ellipseWidth, ellipseHeight);
                        DrawPath(pen, brush, path);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    _dc.DrawRoundedRectangle(
                      brush != null ? brush.RealizeWpfBrush() : null,
                      pen != null ? pen.RealizeWpfPen() : null,
                      new Rect(x, y, width, height), ellipseWidth / 2, ellipseHeight / 2);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawRoundedRectangle(pen, brush, x, y, width, height, ellipseWidth, ellipseHeight);
        }

        // ----- DrawEllipse --------------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, Rectangle rect)
        {
            DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, GdiRectF rect)
        {
            DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, XRect rect)
        {
            DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, double x, double y, double width, double height)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");

            // No DrawArc defined?
            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, 0, 360);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    double radiusX = width / 2;
                    double radiusY = height / 2;
                    _dc.DrawEllipse(null, pen.RealizeWpfPen(), new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawEllipse(pen, null, x, y, width, height);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XBrush brush, Rectangle rect)
        {
            DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XBrush brush, GdiRectF rect)
        {
            DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XBrush brush, XRect rect)
        {
            DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XBrush brush, double x, double y, double width, double height)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.FillEllipse(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    double radiusX = width / 2;
                    double radiusY = height / 2;
                    _dc.DrawEllipse(brush.RealizeWpfBrush(), null, new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawEllipse(null, brush, x, y, width, height);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, XBrush brush, Rectangle rect)
        {
            DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, XBrush brush, GdiRectF rect)
        {
            DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, XBrush brush, XRect rect)
        {
            DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws an ellipse defined by a bounding rectangle.
        /// </summary>
        public void DrawEllipse(XPen pen, XBrush brush, double x, double y, double width, double height)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillEllipse(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height);
                        if (pen != null)
                            _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, 0, 360);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    double radiusX = width / 2;
                    double radiusY = height / 2;
                    _dc.DrawEllipse(
                        brush != null ? brush.RealizeWpfBrush() : null,
                        pen != null ? pen.RealizeWpfPen() : null,
                        new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY);
                }
#endif
#if UWP
                //var cds = new CanvasDrawingSession();
                //cds.DrawCachedGeometry();

                if (TargetContext == XGraphicTargetContext.UWP)
                {
                    var radiusX = (float)width / 2;
                    var radiusY = (float)height / 2;

                    //var geometry = CanvasGeometry.CreateEllipse(_cds.Device, (float)x + radiusX, (float)y + radiusY, radiusX, radiusY);

                    if (brush != null)
                        _cds.FillEllipse((float)x + radiusX, (float)y + radiusY, radiusX, radiusY, Colors.Blue);
                    if (pen != null)
                        _cds.DrawEllipse((float)x + radiusX, (float)y + radiusY, radiusX, radiusY, pen.Color.ToUwpColor());
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawEllipse(pen, brush, x, y, width, height);
        }

        // ----- DrawPolygon --------------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, GdiPoint[] points)
        {
            DrawPolygon(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

#if WPF
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, SysPoint[] points)
        {
            DrawPolygon(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, GdiPointF[] points)
        {
            DrawPolygon(pen, MakeXPointArray(points, 0, points.Length));
        }
#endif

        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, XPoint[] points)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (points == null)
                throw new ArgumentNullException("points");
            if (points.Length < 2)
                throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawPolygon(pen.RealizeGdiPen(), MakePointFArray(points));
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), GeometryHelper.CreatePolygonGeometry(MakePointArray(points), XFillMode.Alternate, true));
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawPolygon(pen, null, points, XFillMode.Alternate);  // XFillMode is ignored
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XBrush brush, GdiPoint[] points, XFillMode fillmode)
        {
            DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XBrush brush, SysPoint[] points, XFillMode fillmode)
        {
            DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XBrush brush, GdiPointF[] points, XFillMode fillmode)
        {
            DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XBrush brush, XPoint[] points, XFillMode fillmode)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");
            if (points == null)
                throw new ArgumentNullException("points");
            if (points.Length < 2)
                throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.FillPolygon(brush.RealizeGdiBrush(), MakePointFArray(points), (FillMode)fillmode);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawGeometry(brush.RealizeWpfBrush(), null, GeometryHelper.CreatePolygonGeometry(MakePointArray(points), fillmode, true));
#endif
            }

            if (_renderer != null)
                _renderer.DrawPolygon(null, brush, points, fillmode);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode)
        {
            DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode)
        {
            DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode)
        {
            DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode);
        }
#endif

        /// <summary>
        /// Draws a polygon defined by an array of points.
        /// </summary>
        public void DrawPolygon(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            if (points == null)
                throw new ArgumentNullException("points");
            if (points.Length < 2)
                throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    GdiPointF[] pts = MakePointFArray(points);
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillPolygon(brush.RealizeGdiBrush(), pts, (FillMode)fillmode);
                        if (pen != null)
                            _gfx.DrawPolygon(pen.RealizeGdiPen(), pts);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null;
                    WpfPen wpfPen = brush != null ? pen.RealizeWpfPen() : null;
                    _dc.DrawGeometry(wpfBrush, wpfPen, GeometryHelper.CreatePolygonGeometry(MakePointArray(points), fillmode, true));
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawPolygon(pen, brush, points, fillmode);
        }

        // ----- DrawPie ------------------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, Rectangle rect, double startAngle, double sweepAngle)
        {
            // ReSharper disable RedundantCast because it is required
            DrawPie(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle);
            // ReSharper restore RedundantCast
        }
#endif

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, GdiRectF rect, double startAngle, double sweepAngle)
        {
            DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }
#endif

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, XRect rect, double startAngle, double sweepAngle)
        {
            DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle)
        {
            if (pen == null)
                throw new ArgumentNullException("pen", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawPie(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    DrawPie(pen, null, x, y, width, height, startAngle, sweepAngle);
#endif
            }

            if (_renderer != null)
                _renderer.DrawPie(pen, null, x, y, width, height, startAngle, sweepAngle);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XBrush brush, Rectangle rect, double startAngle, double sweepAngle)
        {
            // Because of overloading the cast is NOT redundant.
            DrawPie(brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XBrush brush, GdiRectF rect, double startAngle, double sweepAngle)
        {
            DrawPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }
#endif

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XBrush brush, XRect rect, double startAngle, double sweepAngle)
        {
            DrawPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XBrush brush, double x, double y, double width, double height, double startAngle, double sweepAngle)
        {
            if (brush == null)
                throw new ArgumentNullException("brush", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.FillPie(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    DrawPie(null, brush, x, y, width, height, startAngle, sweepAngle);
#endif
            }

            if (_renderer != null)
                _renderer.DrawPie(null, brush, x, y, width, height, startAngle, sweepAngle);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, XBrush brush, Rectangle rect, double startAngle, double sweepAngle)
        {
            DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, XBrush brush, GdiRectF rect, double startAngle, double sweepAngle)
        {
            DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }
#endif

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, XBrush brush, XRect rect, double startAngle, double sweepAngle)
        {
            DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle);
        }

        /// <summary>
        /// Draws a pie defined by an ellipse.
        /// </summary>
        public void DrawPie(XPen pen, XBrush brush, double x, double y, double width, double height, double startAngle, double sweepAngle)
        {
            if (pen == null && brush == null)
                throw new ArgumentNullException("pen", PSSR.NeedPenOrBrush);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillPie(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle);
                        if (pen != null)
                            _gfx.DrawPie(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null;
                    WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null;
                    SysPoint center = new SysPoint(x + width / 2, y + height / 2);
                    SysPoint startArc;
                    ArcSegment arc = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startArc);
                    PathFigure figure = new PathFigure();
                    figure.StartPoint = center;
#if !SILVERLIGHT
                    LineSegment seg = new LineSegment(startArc, true);
#else
                    LineSegment seg = new LineSegment { Point = startArc };
#endif
                    figure.Segments.Add(seg);
                    figure.Segments.Add(arc);
                    figure.IsClosed = true;
                    PathGeometry geo = new PathGeometry();
                    geo.Figures.Add(figure);
                    _dc.DrawGeometry(wpfBrush, wpfPen, geo);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawPie(pen, brush, x, y, width, height, startAngle, sweepAngle);
        }

        // ----- DrawClosedCurve ----------------------------------------------------------------------

        // ----- stroke -----

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, GdiPoint[] points)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, SysPoint[] points)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, GdiPointF[] points)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XPoint[] points)
        {
            DrawClosedCurve(pen, null, points, XFillMode.Alternate, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, GdiPoint[] points, double tension)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, SysPoint[] points, double tension)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, GdiPointF[] points, double tension)
        {
            DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XPoint[] points, double tension)
        {
            DrawClosedCurve(pen, null, points, XFillMode.Alternate, tension);
        }

        // ----- fill -----

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPoint[] points)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, SysPoint[] points)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPointF[] points)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, XPoint[] points)
        {
            DrawClosedCurve(null, brush, points, XFillMode.Alternate, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, SysPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPointF[] points, XFillMode fillmode)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, XPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(null, brush, points, fillmode, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPoint[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, SysPoint[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, GdiPointF[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XBrush brush, XPoint[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(null, brush, points, fillmode, tension);
        }

        // ----- stroke and fill -----

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points)
        {
            DrawClosedCurve(pen, brush, points, XFillMode.Alternate, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode)
        {
            DrawClosedCurve(pen, brush, points, fillmode, 0.5);
        }

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

#if WPF
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

#if GDI
        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode, double tension)
        {
            DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension);
        }
#endif

        /// <summary>
        /// Draws a closed cardinal spline defined by an array of points.
        /// </summary>
        public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode, double tension)
        {
            if (pen == null && brush == null)
            {
                // ReSharper disable once NotResolvedInText
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            }

            int count = points.Length;
            if (count == 0)
                return;
            if (count < 2)
                throw new ArgumentException("Not enough points.", "points");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillClosedCurve(brush.RealizeGdiBrush(), MakePointFArray(points), (FillMode)fillmode, (float)tension);
                        if (pen != null)
                        {
                            // The fillmode is not used by DrawClosedCurve.
                            _gfx.DrawClosedCurve(pen.RealizeGdiPen(), MakePointFArray(points), (float)tension, (FillMode)fillmode);
                        }
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    tension /= 3; // Simply tried out. Not proofed why it is correct.

                    PathFigure figure = new PathFigure();
                    figure.IsClosed = true;
                    figure.StartPoint = new SysPoint(points[0].X, points[0].Y);
                    if (count == 2)
                    {
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension));
                    }
                    else
                    {
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 1], points[0], points[1], points[2], tension));
                        for (int idx = 1; idx < count - 2; idx++)
                            figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension));
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[0], tension));
                        figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 2], points[count - 1], points[0], points[1], tension));
                    }
                    PathGeometry geo = new PathGeometry();
                    geo.FillRule = fillmode == XFillMode.Alternate ? FillRule.EvenOdd : FillRule.Nonzero;
                    geo.Figures.Add(figure);
                    WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null;
                    WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null;
                    _dc.DrawGeometry(wpfBrush, wpfPen, geo);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawClosedCurve(pen, brush, points, tension, fillmode);
        }

        // ----- DrawPath -----------------------------------------------------------------------------

        // ----- stroke -----

        /// <summary>
        /// Draws a graphical path.
        /// </summary>
        public void DrawPath(XPen pen, XGraphicsPath path)
        {
            if (pen == null)
                throw new ArgumentNullException("pen");
            if (path == null)
                throw new ArgumentNullException("path");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.DrawPath(pen.RealizeGdiPen(), path._gdipPath);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawGeometry(null, pen.RealizeWpfPen(), path._pathGeometry);
#endif
            }

            if (_renderer != null)
                _renderer.DrawPath(pen, null, path);
        }

        // ----- fill -----

        /// <summary>
        /// Draws a graphical path.
        /// </summary>
        public void DrawPath(XBrush brush, XGraphicsPath path)
        {
            if (brush == null)
                throw new ArgumentNullException("brush");
            if (path == null)
                throw new ArgumentNullException("path");

            if (_drawGraphics)
            {
#if GDI
                // $TODO THHO Lock???
                if (TargetContext == XGraphicTargetContext.GDI)
                    _gfx.FillPath(brush.RealizeGdiBrush(), path._gdipPath);
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                    _dc.DrawGeometry(brush.RealizeWpfBrush(), null, path._pathGeometry);
#endif
            }

            if (_renderer != null)
                _renderer.DrawPath(null, brush, path);
        }

        // ----- stroke and fill -----

        /// <summary>
        /// Draws a graphical path.
        /// </summary>
        public void DrawPath(XPen pen, XBrush brush, XGraphicsPath path)
        {
            if (pen == null && brush == null)
            {
                // ReSharper disable once NotResolvedInText
                throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush);
            }
            if (path == null)
                throw new ArgumentNullException("path");

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (brush != null)
                            _gfx.FillPath(brush.RealizeGdiBrush(), path._gdipPath);
                        if (pen != null)
                            _gfx.DrawPath(pen.RealizeGdiPen(), path._gdipPath);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null;
                    WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null;
                    _dc.DrawGeometry(wpfBrush, wpfPen, path._pathGeometry);
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawPath(pen, brush, path);
        }

        // ----- DrawString ---------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, GdiPointF point)
        {
            DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), XStringFormats.Default);
        }
#endif

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, XPoint point)
        {
            DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), XStringFormats.Default);
        }

#if GDI
        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, GdiPointF point, XStringFormat format)
        {
            DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), format);
        }
#endif

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, XPoint point, XStringFormat format)
        {
            DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), format);
        }

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, double x, double y)
        {
            DrawString(s, font, brush, new XRect(x, y, 0, 0), XStringFormats.Default);
        }

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, double x, double y, XStringFormat format)
        {
            DrawString(s, font, brush, new XRect(x, y, 0, 0), format);
        }

#if GDI
        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, GdiRectF layoutRectangle)
        {
            DrawString(s, font, brush, new XRect(layoutRectangle), XStringFormats.Default);
        }
#endif

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, XRect layoutRectangle)
        {
            DrawString(s, font, brush, layoutRectangle, XStringFormats.Default);
        }

#if GDI
        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string s, XFont font, XBrush brush, GdiRectF layoutRectangle, XStringFormat format)
        {
            DrawString(s, font, brush, new XRect(layoutRectangle), format);
        }
#endif

        /// <summary>
        /// Draws the specified text string.
        /// </summary>
        public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format)
        {
            if (text == null)
                throw new ArgumentNullException("text");
            if (font == null)
                throw new ArgumentNullException("font");
            if (brush == null)
                throw new ArgumentNullException("brush");

            if (format != null && format.LineAlignment == XLineAlignment.BaseLine && layoutRectangle.Height != 0)
                throw new InvalidOperationException("DrawString: With XLineAlignment.BaseLine the height of the layout rectangle must be 0.");

            if (text.Length == 0)
                return;

            if (format == null)
                format = XStringFormats.Default;
            // format cannot be null below this line.

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    // Was font created with font resolver?
                    if (font.GdiFont == null)
                        throw new InvalidOperationException("This font cannot be used by GDI+.");

                    try
                    {
                        Lock.EnterGdiPlus();
                        GdiRectF rect = layoutRectangle.ToRectangleF();
                        if (format.LineAlignment == XLineAlignment.BaseLine)
                        {
                            double lineSpace = font.GetHeight(); //old: font.GetHeight(this);
                            int cellSpace = font.FontFamily.GetLineSpacing(font.Style);
                            int cellAscent = font.FontFamily.GetCellAscent(font.Style);
                            int cellDescent = font.FontFamily.GetCellDescent(font.Style);
                            double cyAscent = lineSpace * cellAscent / cellSpace;
                            cyAscent = lineSpace * font.CellAscent / font.CellSpace;
                            rect.Offset(0, (float)-cyAscent);
                        }
                        //_gfx.DrawString(text, font.Realize_GdiFont(), brush.RealizeGdiBrush(), rect,
                        //    format != null ? format.RealizeGdiStringFormat() : null);
                        _gfx.DrawString(text, font.GdiFont, brush.RealizeGdiBrush(), rect,
                            format.RealizeGdiStringFormat());
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
#if !SILVERLIGHT
                    double x = layoutRectangle.X;
                    double y = layoutRectangle.Y;

                    double lineSpace = font.GetHeight(); // old: font.GetHeight(this);
                    double cyAscent = lineSpace * font.CellAscent / font.CellSpace;
                    double cyDescent = lineSpace * font.CellDescent / font.CellSpace;

                    bool bold = (font.Style & XFontStyle.Bold) != 0;
                    bool italic = (font.Style & XFontStyle.Italic) != 0;
                    bool strikeout = (font.Style & XFontStyle.Strikeout) != 0;
                    bool underline = (font.Style & XFontStyle.Underline) != 0;

                    //GlyphRun glyphRun=new GlyphRun(font.GlyphTypeface , 0,);
#if DEBUG_
                    if (font.WpfTypeface.FontFamily.Source == "Segoe UI Light")
                        GetType();
#endif
                    FormattedText formattedText = FontHelper.CreateFormattedText(text, font.WpfTypeface, font.Size, brush.RealizeWpfBrush());

                    //formattedText.SetTextDecorations(TextDecorations.OverLine);
                    switch (format.Alignment)
                    {
                        case XStringAlignment.Near:
                            // nothing to do, this is the default
                            //formattedText.TextAlignment = TextAlignment.Left;
                            break;

                        case XStringAlignment.Center:
                            x += layoutRectangle.Width / 2;
                            formattedText.TextAlignment = TextAlignment.Center;
                            break;

                        case XStringAlignment.Far:
                            x += layoutRectangle.Width;
                            formattedText.TextAlignment = TextAlignment.Right;
                            break;
                    }
                    if (PageDirection == XPageDirection.Downwards)
                    {
                        switch (format.LineAlignment)
                        {
                            case XLineAlignment.Near:
                                //y += cyAscent;
                                break;

                            case XLineAlignment.Center:
                                // TODO use CapHeight. PDFlib also uses 3/4 of ascent
                                y += -formattedText.Baseline + (cyAscent * 1 / 3) + layoutRectangle.Height / 2;
                                //y += -formattedText.Baseline + (font.Size * font.Metrics.CapHeight / font.unitsPerEm / 2) + layoutRectangle.Height / 2;
                                break;

                            case XLineAlignment.Far:
                                y += -formattedText.Baseline - cyDescent + layoutRectangle.Height;
                                break;

                            case XLineAlignment.BaseLine:
                                y -= formattedText.Baseline;
                                break;
                        }
                    }
                    else
                    {
                        // TODOWPF: make unit test
                        switch (format.LineAlignment)
                        {
                            case XLineAlignment.Near:
                                //y += cyDescent;
                                break;

                            case XLineAlignment.Center:
                                // TODO use CapHeight. PDFlib also uses 3/4 of ascent
                                //y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2;
                                break;

                            case XLineAlignment.Far:
                                //y += -cyAscent + rect.Height;
                                break;

                            case XLineAlignment.BaseLine:
                                // nothing to do
                                break;
                        }
                    }

                    // BoldSimulation and ItalicSimulation is done only in PDF, not in UI.

                    if (underline)
                    {
                        formattedText.SetTextDecorations(TextDecorations.Underline);
                        //double underlinePosition = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlinePosition / font.cellSpace;
                        //double underlineThickness = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlineThickness / font.cellSpace;
                        //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness);
                    }

                    if (strikeout)
                    {
                        formattedText.SetTextDecorations(TextDecorations.Strikethrough);
                        //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace;
                        //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace;
                        //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize);
                    }

                    //_dc.DrawText(formattedText, layoutRectangle.Location.ToPoint());
                    _dc.DrawText(formattedText, new SysPoint(x, y));
#else
                    _dc.DrawString(this, text, font, brush, layoutRectangle, format);
#endif
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawString(text, font, brush, layoutRectangle, format);
        }

        // ----- MeasureString ------------------------------------------------------------------------

        /// <summary>
        /// Measures the specified string when drawn with the specified font.
        /// </summary>
        public XSize MeasureString(string text, XFont font, XStringFormat stringFormat)
        {
            if (text == null)
                throw new ArgumentNullException("text");
            if (font == null)
                throw new ArgumentNullException("font");
            if (stringFormat == null)
                throw new ArgumentNullException("stringFormat");
#if true
            return FontHelper.MeasureString(text, font, stringFormat);
#else

#if GDI && !WPF
            //XSize gdiSize;  // #MediumTrust
            //if (_gfx != null)
            //    gdiSize = XSize.FromSizeF(_gfx.MeasureString(text, font.Realize_GdiFont(), new GdiPointF(0, 0), stringFormat.RealizeGdiStringFormat()));
            //else
            //    gdiSize = FontHelper.MeasureString(text, font, XStringFormats.Default); // TODO 4STLA: Why is parameter stringFormat not used here?
#if DEBUG_
            XSize edfSize = FontHelper.MeasureString(text, font, XStringFormats.Default);
            //Debug.Assert(gdiSize == edfSize, "Measure string failed.");
            if (gdiSize != edfSize)
            {
                double dx = Math.Abs(gdiSize.Width - edfSize.Width);
                double dy = Math.Abs(gdiSize.Height - edfSize.Height);
                Debug.Assert(dx < .05 * gdiSize.Width, "MeasureString: width differs.");
                Debug.Assert(dy < .05 * gdiSize.Height, "MeasureString: height differs.");
            }
#endif
            return FontHelper.MeasureString(text, font, XStringFormats.Default);
#endif
#if WPF && !GDI
#if !SILVERLIGHT
#if DEBUG
            FormattedText formattedText = FontHelper.CreateFormattedText(text, font.WpfTypeface, font.Size, WpfBrushes.Black);
            XSize size1 = FontHelper.MeasureString(text, font, null);
            XSize size2 = new XSize(formattedText.WidthIncludingTrailingWhitespace, formattedText.Height);
            //Debug.Assert(Math.Abs((size1.Height - size2.Height) * 10) < 1.0);
            return size1;
#else
            // Same as above, but without code needed for Debug.Assert.
            XSize size1 = FontHelper.MeasureString(text, font, null);
            return size1;
#endif
#else
            // Use the WPF code also for Silverlight.
            XSize size1 = FontHelper.MeasureString(text, font, null);
            return size1;
#endif

#endif
#if WPF && GDI
#if true_
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                XSize gdiSize = XSize.FromSizeF(_gfx.MeasureString(text, font.Realize_GdiFont(), new GdiPointF(0, 0), stringFormat.RealizeGdiStringFormat()));
#if DEBUG
#if GDI
                {
                    //Debug.WriteLine(gdiSize);
                    XSize edfSize = FontHelper14.MeasureStringGdi(_gfx, text, font, XStringFormats.Default);
                    //Debug.WriteLine(edfSize);
                    //Debug.Assert(gdiSize == edfSize, "Measure string failed.");
                    if (gdiSize.Width != edfSize.Width)
                    {
                        Debug.WriteLine(String.Format("Width: {0}, {1} : {2}", gdiSize.Width, edfSize.Width, gdiSize.Width / edfSize.Width));
                    }
                    if (gdiSize.Height != edfSize.Height)
                    {
                        Debug.WriteLine(String.Format("Height: {0}, {1}", gdiSize.Height, edfSize.Height));
                    }

                    //double lineSpace = font.GetHeight(this);
                    //int cellSpace = font.cellSpace; // font.FontFamily.GetLineSpacing(font.Style);
                    //int cellAscent = font.cellAscent; // font.FontFamily.GetCellAscent(font.Style);
                    //int cellDescent = font.cellDescent; // font.FontFamily.GetCellDescent(font.Style);
                    //double cyAscent = lineSpace * cellAscent / cellSpace;
                    //double cyDescent = lineSpace * cellDescent / cellSpace;
                }
#endif
#if WPF
                {
                    //Debug.WriteLine(gdiSize);
                    XSize edfSize = FontHelper14.MeasureStringWpf(text, font, XStringFormats.Default);
                    //Debug.WriteLine(edfSize);
                    //Debug.Assert(gdiSize == edfSize, "Measure string failed.");
                    if (gdiSize.Width != edfSize.Width)
                    {
                        Debug.WriteLine(String.Format("Width: {0}, {1} : {2}", gdiSize.Width, edfSize.Width, gdiSize.Width / edfSize.Width));
                    }
                    if (gdiSize.Height != edfSize.Height)
                    {
                        Debug.WriteLine(String.Format("Height: {0}, {1}", gdiSize.Height, edfSize.Height));
                    }

                    //double lineSpace = font.GetHeight(this);
                    //int cellSpace = font.cellSpace; // font.FontFamily.GetLineSpacing(font.Style);
                    //int cellAscent = font.cellAscent; // font.FontFamily.GetCellAscent(font.Style);
                    //int cellDescent = font.cellDescent; // font.FontFamily.GetCellDescent(font.Style);
                    //double cyAscent = lineSpace * cellAscent / cellSpace;
                    //double cyDescent = lineSpace * cellDescent / cellSpace;
                }
#endif
#endif
                return gdiSize;
            }
            if (TargetContext == XGraphicTargetContext.WPF)
            {
                //double h = font.Height;
                //FormattedText formattedText = new FormattedText(text, new CultureInfo("en-us"),
                //  FlowDirection.LeftToRight, font.typeface, font.Size, WpfBrushes.Black);
                FormattedText formattedText = FontHelper.CreateFormattedText(text, font.Typeface, font.Size, WpfBrushes.Black);
                XSize wpfSize = new XSize(formattedText.WidthIncludingTrailingWhitespace, formattedText.Height);
#if DEBUG
                Debug.WriteLine(wpfSize);
#endif
                return wpfSize;
            }
            Debug.Assert(false);
            return XSize.Empty;
#else
            XSize size23 = FontHelper.MeasureString(text, font, XStringFormats.Default);
            return size23;
#endif
#endif
#if CORE || NETFX_CORE || UWP
            XSize size = FontHelper.MeasureString(text, font, XStringFormats.Default);
            return size;
#endif
#endif
        }

        /// <summary>
        /// Measures the specified string when drawn with the specified font.
        /// </summary>
        public XSize MeasureString(string text, XFont font)
        {
            return MeasureString(text, font, XStringFormats.Default);
        }

        // ----- DrawImage ----------------------------------------------------------------------------

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, GdiPoint point)
        {
            DrawImage(image, point.X, point.Y);
        }
#endif

#if WPF
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, SysPoint point)
        {
            DrawImage(image, point.X, point.Y);
        }
#endif

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, GdiPointF point)
        {
            DrawImage(image, point.X, point.Y);
        }
#endif

        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, XPoint point)
        {
            DrawImage(image, point.X, point.Y);
        }

        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, double x, double y)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            CheckXPdfFormConsistence(image);

            double width = image.PointWidth;
            double height = image.PointHeight;

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (image._gdiImage != null)
                        {
                            InterpolationMode interpolationMode = InterpolationMode.Invalid;
                            if (!image.Interpolate)
                            {
                                interpolationMode = _gfx.InterpolationMode;
                                _gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
                            }

                            _gfx.DrawImage(image._gdiImage, (float)x, (float)y, (float)width, (float)height);

                            if (!image.Interpolate)
                                _gfx.InterpolationMode = interpolationMode;
                        }
                        else
                        {
                            DrawMissingImageRect(new XRect(x, y, width, height));
                            //_gfx.DrawRectangle(Pens.Red, (float)x, (float)y, (float)width, (float)height);
                            //_gfx.DrawLine(Pens.Red, (float)x, (float)y, (float)(x + width), (float)(y + height));
                            //_gfx.DrawLine(Pens.Red, (float)(x + width), (float)y, (float)x, (float)(y + height));
                        }
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    if (image._wpfImage != null)
                    {
                        _dc.DrawImage(image._wpfImage, new Rect(x, y, image.PointWidth, image.PointHeight));
                    }
                    else
                    {
                        DrawMissingImageRect(new XRect(x, y, width, height));
                    }
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawImage(image, x, y, image.PointWidth, image.PointHeight);
            //image.Width * 72 / image.HorizontalResolution,
            //image.Height * 72 / image.HorizontalResolution);
        }

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, Rectangle rect)
        {
            // Because of overloading the cast is NOT redundant.
            DrawImage(image, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height);
        }
#endif

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, GdiRectF rect)
        {
            DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height);
        }
#endif

        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, XRect rect)
        {
            DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height);
        }

        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, double x, double y, double width, double height)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            CheckXPdfFormConsistence(image);

            if (_drawGraphics)
            {
                // THHO4STLA: Platform-independent images cannot be drawn here, can they?    => They can. Lazy create platform-dependent image and draw that.
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (image._gdiImage != null)
                        {
                            InterpolationMode interpolationMode = InterpolationMode.Invalid;
                            if (!image.Interpolate)
                            {
                                interpolationMode = _gfx.InterpolationMode;
                                _gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
                            }

                            _gfx.DrawImage(image._gdiImage, (float)x, (float)y, (float)width, (float)height);

                            if (!image.Interpolate)
                                _gfx.InterpolationMode = interpolationMode;
                        }
                        else
                        {
                            XImage placeholder = null;
                            XPdfForm pdfForm = image as XPdfForm;
                            if (pdfForm != null)
                            {
                                //XPdfForm pf = pdfForm;
                                if (pdfForm.PlaceHolder != null)
                                    placeholder = pdfForm.PlaceHolder;
                            }
                            if (placeholder != null)
                                _gfx.DrawImage(placeholder._gdiImage, (float)x, (float)y, (float)width,
                                    (float)height);
                            else
                            {
                                DrawMissingImageRect(new XRect(x, y, width, height));
                            }
                        }
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    if (image._wpfImage != null)
                    {
                        //InterpolationMode interpolationMode = InterpolationMode.Invalid;
                        //if (!image.Interpolate)
                        //{
                        //  interpolationMode = gfx.InterpolationMode;
                        //  gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
                        //}

                        _dc.DrawImage(image._wpfImage, new Rect(x, y, width, height));

                        //if (!image.Interpolate)
                        //  gfx.InterpolationMode = interpolationMode;
                    }
                    else
                    {
                        XImage placeholder = null;
                        if (image is XPdfForm)
                        {
                            XPdfForm pf = image as XPdfForm;
                            if (pf.PlaceHolder != null)
                                placeholder = pf.PlaceHolder;
                        }
                        if (placeholder != null)
                            _dc.DrawImage(placeholder._wpfImage, new Rect(x, y, width, height));
                        else
                            DrawMissingImageRect(new XRect(x, y, width, height));
                    }
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawImage(image, x, y, width, height);
        }

        // TODO: calculate destination size
        //public void DrawImage(XImage image, double x, double y, GdiRectF srcRect, XGraphicsUnit srcUnit)
        //public void DrawImage(XImage image, double x, double y, XRect srcRect, XGraphicsUnit srcUnit)

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, Rectangle destRect, Rectangle srcRect, XGraphicsUnit srcUnit)
        {
            XRect destRectX = new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height);
            XRect srcRectX = new XRect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height);
            DrawImage(image, destRectX, srcRectX, srcUnit);
        }
#endif

#if GDI
        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, GdiRectF destRect, GdiRectF srcRect, XGraphicsUnit srcUnit)
        {
            XRect destRectX = new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height);
            XRect srcRectX = new XRect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height);
            DrawImage(image, destRectX, srcRectX, srcUnit);
        }
#endif

        /// <summary>
        /// Draws the specified image.
        /// </summary>
        public void DrawImage(XImage image, XRect destRect, XRect srcRect, XGraphicsUnit srcUnit)
        {
            if (image == null)
                throw new ArgumentNullException("image");

            CheckXPdfFormConsistence(image);

            if (_drawGraphics)
            {
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        if (image._gdiImage != null)
                        {
                            InterpolationMode interpolationMode = InterpolationMode.Invalid;
                            if (!image.Interpolate)
                            {
                                interpolationMode = _gfx.InterpolationMode;
                                _gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
                            }

                            GdiRectF destRectF = new GdiRectF((float)destRect.X, (float)destRect.Y,
                                (float)destRect.Width, (float)destRect.Height);
                            GdiRectF srcRectF = new GdiRectF((float)srcRect.X, (float)srcRect.Y,
                                (float)srcRect.Width, (float)srcRect.Height);
                            _gfx.DrawImage(image._gdiImage, destRectF, srcRectF, GraphicsUnit.Pixel);

                            if (!image.Interpolate)
                                _gfx.InterpolationMode = interpolationMode;
                        }
                        else
                        {
                            DrawMissingImageRect(new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height));
                        }
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                if (TargetContext == XGraphicTargetContext.WPF)
                {
                    if (image._wpfImage != null)
                    {
                        //InterpolationMode interpolationMode = InterpolationMode.Invalid;
                        //if (!image.Interpolate)
                        //{
                        //  interpolationMode = gfx.InterpolationMode;
                        //  //gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
                        //}

                        // HACK: srcRect is ignored
                        //double x = destRect.X;
                        //double y = destRect.Y;
                        _dc.DrawImage(image._wpfImage, new SysRect(destRect.X, destRect.Y, destRect.Width, destRect.Height));

                        //if (!image.Interpolate)
                        //  gfx.InterpolationMode = interpolationMode;
                    }
                    else
                    {
                        DrawMissingImageRect(destRect);
                    }
                }
#endif
            }

            if (_renderer != null)
                _renderer.DrawImage(image, destRect, srcRect, srcUnit);
        }

        //TODO?
        //public void DrawImage(XImage image, Rectangle destRect, double srcX, double srcY, double srcWidth, double srcHeight, GraphicsUnit srcUnit);
        //public void DrawImage(XImage image, Rectangle destRect, double srcX, double srcY, double srcWidth, double srcHeight, GraphicsUnit srcUnit);

        void DrawMissingImageRect(XRect rect)
        {
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    float x = (float)rect.X;
                    float y = (float)rect.Y;
                    float width = (float)rect.Width;
                    float height = (float)rect.Height;
                    _gfx.DrawRectangle(Pens.Red, x, y, width, height);
                    _gfx.DrawLine(Pens.Red, x, y, x + width, y + height);
                    _gfx.DrawLine(Pens.Red, x + width, y, x, y + height);
                }
                finally { Lock.ExitGdiPlus(); }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
            {
                double x = rect.X;
                double y = rect.Y;
                double width = rect.Width;
                double height = rect.Height;
#if !SILVERLIGHT
                WpfPen pen = new WpfPen(WpfBrushes.Red, 1);
#else
                WpfPen pen = new WpfPen();
                pen.Brush = new SolidColorBrush(Colors.Red);
                pen.Thickness = 1;
#endif
                _dc.DrawRectangle(null, pen, new Rect(x, y, width, height));
                _dc.DrawLine(pen, new SysPoint(x, y), new SysPoint(x + width, y + height));
                _dc.DrawLine(pen, new SysPoint(x + width, y), new SysPoint(x, y + height));
            }
#endif
        }

        /// <summary>
        /// Checks whether drawing is allowed and disposes the XGraphics object, if necessary.
        /// </summary>
        void CheckXPdfFormConsistence(XImage image)
        {
            XForm xForm = image as XForm;
            if (xForm != null)
            {
                // Force disposing of XGraphics that draws the content
                xForm.Finish();

                // ReSharper disable once MergeSequentialChecks
                if (_renderer != null && (_renderer as XGraphicsPdfRenderer) != null)
                {
                    if (xForm.Owner != null && xForm.Owner != ((XGraphicsPdfRenderer)_renderer).Owner)
                        throw new InvalidOperationException(
                            "A XPdfForm object is always bound to the document it was created for and cannot be drawn in the context of another document.");

                    if (xForm == ((XGraphicsPdfRenderer)_renderer)._form)
                        throw new InvalidOperationException(
                            "A XPdfForm cannot be drawn on itself.");
                }
            }
        }

        // ----- DrawBarCode --------------------------------------------------------------------------

        /// <summary>
        /// Draws the specified bar code.
        /// </summary>
        public void DrawBarCode(BarCodes.BarCode barcode, XPoint position)
        {
            barcode.Render(this, XBrushes.Black, null, position);
        }

        /// <summary>
        /// Draws the specified bar code.
        /// </summary>
        public void DrawBarCode(BarCodes.BarCode barcode, XBrush brush, XPoint position)
        {
            barcode.Render(this, brush, null, position);
        }

        /// <summary>
        /// Draws the specified bar code.
        /// </summary>
        public void DrawBarCode(BarCodes.BarCode barcode, XBrush brush, XFont font, XPoint position)
        {
            barcode.Render(this, brush, font, position);
        }

        // ----- DrawMatrixCode -----------------------------------------------------------------------

        /// <summary>
        /// Draws the specified data matrix code.
        /// </summary>
        public void DrawMatrixCode(BarCodes.MatrixCode matrixcode, XPoint position)
        {
            matrixcode.Render(this, XBrushes.Black, position);
        }

        /// <summary>
        /// Draws the specified data matrix code.
        /// </summary>
        public void DrawMatrixCode(BarCodes.MatrixCode matrixcode, XBrush brush, XPoint position)
        {
            matrixcode.Render(this, brush, position);
        }

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Save and Restore

        /// <summary>
        /// Saves the current state of this XGraphics object and identifies the saved state with the
        /// returned XGraphicsState object.
        /// </summary>
        public XGraphicsState Save()
        {
            XGraphicsState xState = null;
#if CORE || NETFX_CORE
            if (TargetContext == XGraphicTargetContext.CORE || TargetContext == XGraphicTargetContext.NONE)
            {
                xState = new XGraphicsState();
                InternalGraphicsState iState = new InternalGraphicsState(this, xState);
                iState.Transform = _transform;
                _gsStack.Push(iState);
            }
            else
            {
                Debug.Assert(false, "XGraphicTargetContext must be XGraphicTargetContext.CORE.");
            }
#endif
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    xState = new XGraphicsState(_gfx != null ? _gfx.Save() : null);
                    InternalGraphicsState iState = new InternalGraphicsState(this, xState);
                    iState.Transform = _transform;
                    _gsStack.Push(iState);
                }
                finally { Lock.ExitGdiPlus(); }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
            {
                xState = new XGraphicsState();
                InternalGraphicsState iState = new InternalGraphicsState(this, xState);
                iState.Transform = _transform;
                _gsStack.Push(iState);
            }
#endif

            if (_renderer != null)
                _renderer.Save(xState);

            return xState;
        }

        /// <summary>
        /// Restores the state of this XGraphics object to the state represented by the specified 
        /// XGraphicsState object.
        /// </summary>
        public void Restore(XGraphicsState state)
        {
            if (state == null)
                throw new ArgumentNullException("state");

#if CORE
            if (TargetContext == XGraphicTargetContext.CORE)
            {
                _gsStack.Restore(state.InternalState);
                _transform = state.InternalState.Transform;
            }
#endif
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    _gsStack.Restore(state.InternalState);
                    if (_gfx != null)
                        _gfx.Restore(state.GdiState);
                    _transform = state.InternalState.Transform;
                }
                finally { Lock.ExitGdiPlus(); }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
            {
                _gsStack.Restore(state.InternalState);
                _transform = state.InternalState.Transform;
            }
#endif

            if (_renderer != null)
                _renderer.Restore(state);
        }

        /// <summary>
        /// Restores the state of this XGraphics object to the state before the most recently call of Save.
        /// </summary>
        public void Restore()
        {
            if (_gsStack.Count == 0)
                throw new InvalidOperationException("Cannot restore without preceding save operation.");
            Restore(_gsStack.Current.State);
        }

        /// <summary>
        /// Saves a graphics container with the current state of this XGraphics and 
        /// opens and uses a new graphics container.
        /// </summary>
        public XGraphicsContainer BeginContainer()
        {
            return BeginContainer(new XRect(0, 0, 1, 1), new XRect(0, 0, 1, 1), XGraphicsUnit.Point);
        }

#if GDI
        /// <summary>
        /// Saves a graphics container with the current state of this XGraphics and 
        /// opens and uses a new graphics container.
        /// </summary>
        public XGraphicsContainer BeginContainer(Rectangle dstrect, Rectangle srcrect, XGraphicsUnit unit)
        {
            return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit);
        }
#endif

#if GDI
        /// <summary>
        /// Saves a graphics container with the current state of this XGraphics and 
        /// opens and uses a new graphics container.
        /// </summary>
        public XGraphicsContainer BeginContainer(GdiRectF dstrect, GdiRectF srcrect, XGraphicsUnit unit)
        {
            return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit);
        }
#endif

#if WPF
        /// <summary>
        /// Saves a graphics container with the current state of this XGraphics and 
        /// opens and uses a new graphics container.
        /// </summary>
        public XGraphicsContainer BeginContainer(Rect dstrect, Rect srcrect, XGraphicsUnit unit)
        {
            return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit);
        }
#endif

        /// <summary>
        /// Saves a graphics container with the current state of this XGraphics and 
        /// opens and uses a new graphics container.
        /// </summary>
        public XGraphicsContainer BeginContainer(XRect dstrect, XRect srcrect, XGraphicsUnit unit)
        {
            // TODO: unit
            if (unit != XGraphicsUnit.Point)
                throw new ArgumentException("The current implementation supports XGraphicsUnit.Point only.", "unit");

            XGraphicsContainer xContainer = null;
#if CORE
            if (TargetContext == XGraphicTargetContext.CORE)
                xContainer = new XGraphicsContainer();
#endif
#if GDI
            // _gfx can be null if drawing applies to PDF page only.
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    xContainer = new XGraphicsContainer(_gfx != null ? _gfx.Save() : null);
                }
                finally { Lock.ExitGdiPlus(); }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
                xContainer = new XGraphicsContainer();
#endif
            InternalGraphicsState iState = new InternalGraphicsState(this, xContainer);
            iState.Transform = _transform;

            _gsStack.Push(iState);

            if (_renderer != null)
                _renderer.BeginContainer(xContainer, dstrect, srcrect, unit);

            XMatrix matrix = new XMatrix();
            double scaleX = dstrect.Width / srcrect.Width;
            double scaleY = dstrect.Height / srcrect.Height;
            matrix.TranslatePrepend(-srcrect.X, -srcrect.Y);
            matrix.ScalePrepend(scaleX, scaleY);
            matrix.TranslatePrepend(dstrect.X / scaleX, dstrect.Y / scaleY);
            AddTransform(matrix, XMatrixOrder.Prepend);

            return xContainer;
        }

        /// <summary>
        /// Closes the current graphics container and restores the state of this XGraphics 
        /// to the state saved by a call to the BeginContainer method.
        /// </summary>
        public void EndContainer(XGraphicsContainer container)
        {
            if (container == null)
                throw new ArgumentNullException("container");

            _gsStack.Restore(container.InternalState);
#if CORE
            // nothing to do
#endif
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI && _gfx != null)
            {
                try
                {
                    Lock.EnterGdiPlus();
                    _gfx.Restore(container.GdiState);
                }
                finally { Lock.ExitGdiPlus(); }
            }
#endif
#if WPF
            // nothing to do
#endif
            _transform = container.InternalState.Transform;

            if (_renderer != null)
                _renderer.EndContainer(container);
        }

        /// <summary>
        /// Gets the current graphics state level. The default value is 0. Each call of Save or BeginContainer
        /// increased and each call of Restore or EndContainer decreased the value by 1.
        /// </summary>
        public int GraphicsStateLevel
        {
            get { return _gsStack.Count; }
        }

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Properties

        /// <summary>
        /// Gets or sets the smoothing mode.
        /// </summary>
        /// <value>The smoothing mode.</value>
        public XSmoothingMode SmoothingMode
        {
            get
            {
#if CORE
                // nothing to do
#endif
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI &&
                    _gfx != null)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        return (XSmoothingMode)_gfx.SmoothingMode;
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                // nothing to do
#endif
                return _smoothingMode;
            }
            set
            {
                _smoothingMode = value;
#if CORE
                // nothing to do
#endif
#if GDI
                if (TargetContext == XGraphicTargetContext.GDI &&
                    _gfx != null)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.SmoothingMode = (SmoothingMode)value;
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF
                // nothing to do
#endif
            }
        }
        XSmoothingMode _smoothingMode;

        //public Region Clip { get; set; }
        //public GdiRectF ClipBounds { get; }
        //public CompositingMode CompositingMode { get; set; }
        //public CompositingQuality CompositingQuality { get; set; }
        //public float DpiX { get; }
        //public float DpiY { get; }
        //public InterpolationMode InterpolationMode { get; set; }
        //public bool IsClipEmpty { get; }
        //public bool IsVisibleClipEmpty { get; }
        //public float PageScale { get; set; }
        //public GraphicsUnit PageUnit { get; set; }
        //public PixelOffsetMode PixelOffsetMode { get; set; }
        //public Point RenderingOrigin { get; set; }
        //public SmoothingMode SmoothingMode { get; set; }
        //public int TextContrast { get; set; }
        //public TextRenderingHint TextRenderingHint { get; set; }
        //public Matrix Transform { get; set; }
        //public GdiRectF VisibleClipBounds { get; }

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Transformation

        /// <summary>
        /// Applies the specified translation operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void TranslateTransform(double dx, double dy)
        {
            AddTransform(XMatrix.CreateTranslation(dx, dy), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified translation operation to the transformation matrix of this object
        /// in the specified order.
        /// </summary>
        public void TranslateTransform(double dx, double dy, XMatrixOrder order)
        {
            XMatrix matrix = new XMatrix();
            matrix.TranslatePrepend(dx, dy);
            AddTransform(matrix, order);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void ScaleTransform(double scaleX, double scaleY)
        {
            AddTransform(XMatrix.CreateScaling(scaleX, scaleY), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object
        /// in the specified order.
        /// </summary>
        public void ScaleTransform(double scaleX, double scaleY, XMatrixOrder order)
        {
            XMatrix matrix = new XMatrix();
            matrix.ScalePrepend(scaleX, scaleY);
            AddTransform(matrix, order);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        // ReSharper disable once InconsistentNaming
        public void ScaleTransform(double scaleXY)
        {
            ScaleTransform(scaleXY, scaleXY);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object
        /// in the specified order.
        /// </summary>
        // ReSharper disable once InconsistentNaming
        public void ScaleTransform(double scaleXY, XMatrixOrder order)
        {
            ScaleTransform(scaleXY, scaleXY, order);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void ScaleAtTransform(double scaleX, double scaleY, double centerX, double centerY)
        {
            AddTransform(XMatrix.CreateScaling(scaleX, scaleY, centerX, centerY), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified scaling operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void ScaleAtTransform(double scaleX, double scaleY, XPoint center)
        {
            AddTransform(XMatrix.CreateScaling(scaleX, scaleY, center.X, center.Y), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified rotation operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void RotateTransform(double angle)
        {
            AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified rotation operation to the transformation matrix of this object
        /// in the specified order. The angle unit of measure is degree.
        /// </summary>
        public void RotateTransform(double angle, XMatrixOrder order)
        {
            XMatrix matrix = new XMatrix();
            matrix.RotatePrepend(angle);
            AddTransform(matrix, order);
        }

        /// <summary>
        /// Applies the specified rotation operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void RotateAtTransform(double angle, XPoint point)
        {
            AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified rotation operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// </summary>
        public void RotateAtTransform(double angle, XPoint point, XMatrixOrder order)
        {
            AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y), order);
        }

        /// <summary>
        /// Applies the specified shearing operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// ShearTransform is a synonym for SkewAtTransform.
        /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis.
        /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis.
        /// </summary>
        public void ShearTransform(double shearX, double shearY)
        {
            AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified shearing operation to the transformation matrix of this object
        /// in the specified order.
        /// ShearTransform is a synonym for SkewAtTransform.
        /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis.
        /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis.
        /// </summary>
        public void ShearTransform(double shearX, double shearY, XMatrixOrder order)
        {
            AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad), order);
        }

        /// <summary>
        /// Applies the specified shearing operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// ShearTransform is a synonym for SkewAtTransform.
        /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis.
        /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis.
        /// </summary>
        public void SkewAtTransform(double shearX, double shearY, double centerX, double centerY)
        {
            AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad, centerX, centerY), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Applies the specified shearing operation to the transformation matrix of this object by 
        /// prepending it to the object's transformation matrix.
        /// ShearTransform is a synonym for SkewAtTransform.
        /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis.
        /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis.
        /// </summary>
        public void SkewAtTransform(double shearX, double shearY, XPoint center)
        {
            AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad, center.X, center.Y), XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Multiplies the transformation matrix of this object and specified matrix.
        /// </summary>
        public void MultiplyTransform(XMatrix matrix)
        {
            AddTransform(matrix, XMatrixOrder.Prepend);
        }

        /// <summary>
        /// Multiplies the transformation matrix of this object and specified matrix in the specified order.
        /// </summary>
        public void MultiplyTransform(XMatrix matrix, XMatrixOrder order)
        {
            AddTransform(matrix, order);
        }

        /// <summary>
        /// Gets the current transformation matrix.
        /// The transformation matrix cannot be set. Instead use Save/Restore or BeginContainer/EndContainer to
        /// save the state before Transform is called and later restore to the previous transform.
        /// </summary>
        public XMatrix Transform
        {
            get { return _transform; }
        }

        /// <summary>
        /// Applies a new transformation to the current transformation matrix.
        /// </summary>
        void AddTransform(XMatrix transform, XMatrixOrder order)
        {
            XMatrix matrix = _transform;
            matrix.Multiply(transform, order);
            _transform = matrix;
            matrix = DefaultViewMatrix;
            matrix.Multiply(_transform, XMatrixOrder.Prepend);
#if CORE
            if (TargetContext == XGraphicTargetContext.CORE)
            {
                GetType();
                // TODO: _gsStack...
            }
#endif
#if GDI
            if (TargetContext == XGraphicTargetContext.GDI)
            {
                if (_gfx != null)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.Transform = (GdiMatrix)matrix;
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
            }
#endif
#if WPF
            if (TargetContext == XGraphicTargetContext.WPF)
            {
#if !SILVERLIGHT
                MatrixTransform mt = new MatrixTransform(transform.ToWpfMatrix());
#else
                MatrixTransform mt = new MatrixTransform();
                mt.Matrix = transform.ToWpfMatrix();
#endif
                if (order == XMatrixOrder.Append)
                    mt = (MatrixTransform)mt.Inverse;
                _gsStack.Current.PushTransform(mt);
            }
#endif
            if (_renderer != null)
                _renderer.AddTransform(transform, XMatrixOrder.Prepend);
        }

        //public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] points)
        //{
        //}
        //
        //public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, GdiPointF[] points)
        //{
        //}

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Clipping

#if GDI
        /// <summary>
        /// Updates the clip region of this XGraphics to the intersection of the 
        /// current clip region and the specified rectangle.
        /// </summary>
        public void IntersectClip(Rectangle rect)
        {
            XGraphicsPath path = new XGraphicsPath();
            path.AddRectangle(rect);
            IntersectClip(path);
        }
#endif

#if GDI
        /// <summary>
        /// Updates the clip region of this XGraphics to the intersection of the 
        /// current clip region and the specified rectangle.
        /// </summary>
        public void IntersectClip(GdiRectF rect)
        {
            XGraphicsPath path = new XGraphicsPath();
            path.AddRectangle(rect);
            IntersectClip(path);
        }
#endif

        /// <summary>
        /// Updates the clip region of this XGraphics to the intersection of the 
        /// current clip region and the specified rectangle.
        /// </summary>
        public void IntersectClip(XRect rect)
        {
            XGraphicsPath path = new XGraphicsPath();
            path.AddRectangle(rect);
            IntersectClip(path);
        }

        /// <summary>
        /// Updates the clip region of this XGraphics to the intersection of the 
        /// current clip region and the specified graphical path.
        /// </summary>
        public void IntersectClip(XGraphicsPath path)
        {
            if (path == null)
                throw new ArgumentNullException("path");

            if (_drawGraphics)
            {
#if GDI && !WPF
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.SetClip(path._gdipPath, CombineMode.Intersect);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
#endif
#if WPF && !GDI
                if (TargetContext == XGraphicTargetContext.WPF)
                    _gsStack.Current.PushClip(path._pathGeometry);
#endif
#if GDI && WPF
                if (TargetContext == XGraphicTargetContext.GDI)
                {
                    try
                    {
                        Lock.EnterGdiPlus();
                        _gfx.SetClip(path._gdipPath, CombineMode.Intersect);
                    }
                    finally { Lock.ExitGdiPlus(); }
                }
                else
                {
                    _gsStack.Current.PushClip(path._pathGeometry);
                }
#endif
            }

            if (_renderer != null)
                _renderer.SetClip(path, XCombineMode.Intersect);
        }

        //public void SetClip(Graphics g);
        //public void SetClip(Graphics g, CombineMode combineMode);
        //public void SetClip(GraphicsPath path, CombineMode combineMode);
        //public void SetClip(Rectangle rect, CombineMode combineMode);
        //public void SetClip(GdiRectF rect, CombineMode combineMode);
        //public void SetClip(Region region, CombineMode combineMode);
        //public void IntersectClip(Region region);
        //public void ExcludeClip(Region region);

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Miscellaneous

        /// <summary>
        /// Writes a comment to the output stream. Comments have no effect on the rendering of the output.
        /// They may be useful to mark a position in a content stream of a PDF document.
        /// </summary>
        public void WriteComment(string comment)
        {
            if (comment == null)
                throw new ArgumentNullException("comment");

            if (_drawGraphics)
            {
                // TODO: Do something if metafile?
            }

            if (_renderer != null)
                _renderer.WriteComment(comment);
        }

        /// <summary>
        /// Permits access to internal data.
        /// </summary>
        public XGraphicsInternals Internals
        {
            get { return _internals ?? (_internals = new XGraphicsInternals(this)); }
        }
        XGraphicsInternals _internals;

        /// <summary>
        /// (Under construction. May change in future versions.)
        /// </summary>
        public SpaceTransformer Transformer
        {
            get { return _transformer ?? (_transformer = new SpaceTransformer(this)); }
        }
        SpaceTransformer _transformer;

        #endregion

        // --------------------------------------------------------------------------------------------

        #region Internal Helper Functions

#if GDI
        /// <summary>
        /// Converts a GdiPoint[] into a GdiPointF[].
        /// </summary>
        internal static GdiPointF[] MakePointFArray(GdiPoint[] points, int offset, int count)
        {
            if (points == null)
                return null;

            //int length = points.Length;
            GdiPointF[] result = new GdiPointF[count];
            for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++)
            {
                result[idx].X = points[srcIdx].X;
                result[idx].Y = points[srcIdx].Y;
            }
            return result;
        }
#endif

#if GDI
        /// <summary>
        /// Converts a XPoint[] into a GdiPointF[].
        /// </summary>
        internal static GdiPointF[] MakePointFArray(XPoint[] points)
        {
            if (points == null)
                return null;

            int count = points.Length;
            GdiPointF[] result = new GdiPointF[count];
            for (int idx = 0; idx < count; idx++)
            {
                result[idx].X = (float)points[idx].X;
                result[idx].Y = (float)points[idx].Y;
            }
            return result;
        }
#endif

#if GDI
        /// <summary>
        /// Converts a Point[] into a XPoint[].
        /// </summary>
        internal static XPoint[] MakeXPointArray(GdiPoint[] points, int offset, int count)
        {
            if (points == null)
                return null;

            //int lengh = points.Length;
            XPoint[] result = new XPoint[count];
            for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++)
            {
                result[idx].X = points[srcIdx].X;
                result[idx].Y = points[srcIdx].Y;
            }
            return result;
        }
#endif

#if WPF || NETFX_CORE
        /// <summary>
        /// Converts a Point[] into a XPoint[].
        /// </summary>
        internal static XPoint[] MakeXPointArray(SysPoint[] points, int offset, int count)
        {
            if (points == null)
                return null;

            //int length = points.Length;
            XPoint[] result = new XPoint[count];
            for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++)
            {
                result[idx].X = points[srcIdx].X;
                result[idx].Y = points[srcIdx].Y;
            }
            return result;
        }
#endif

#if GDI
        /// <summary>
        /// Converts a GdiPointF[] into a XPoint[].
        /// </summary>
        internal static XPoint[] MakeXPointArray(GdiPointF[] points, int offset, int count)
        {
            if (points == null)
                return null;

            //int length = points.Length;
            XPoint[] result = new XPoint[count];
            for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++)
            {
                result[idx].X = points[srcIdx].X;
                result[idx].Y = points[srcIdx].Y;
            }
            return result;
        }
#endif

#if GDI
        /// <summary>
        /// Converts a XRect[] into a GdiRectF[].
        /// </summary>
        internal static GdiRectF[] MakeRectangleFArray(XRect[] rects, int offset, int count)
        {
            if (rects == null)
                return null;

            //int length = rects.Length;
            GdiRectF[] result = new GdiRectF[count];
            for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++)
            {
                XRect rect = rects[srcIdx];
                result[idx] = new GdiRectF((float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height);
            }
            return result;
        }
#endif

#if WPF || NETFX_CORE
        /// <summary>
        /// Converts an XPoint[] into a Point[].
        /// </summary>
        internal static SysPoint[] MakePointArray(XPoint[] points)
        {
            if (points == null)
                return null;

            int count = points.Length;
            SysPoint[] result = new SysPoint[count];
            for (int idx = 0; idx < count; idx++)
            {
                result[idx].X = points[idx].X;
                result[idx].Y = points[idx].Y;
            }
            return result;
        }
#endif

        #endregion

        ///// <summary>
        ///// Testcode
        ///// </summary>
        //public void TestXObject(PdfDocument thisDoc, PdfPage thisPage, int page,
        //      PdfDocument externalDoc, ImportedObjectTable impDoc)
        //{
        //    PdfPage impPage = externalDoc.Pages[page];
        //    //      impDoc.ImportPage(impPage);
        //    PdfFormXObject form = new PdfFormXObject(thisDoc, impDoc, impPage);
        //    thisDoc.xrefTable.Add(form);

        //    PdfDictionary xobjects = new PdfDictionary();
        //    xobjects.Elements["/X42"] = form.XRef;
        //    thisPage.Resources.Elements[PdfResources.Keys.XObject] = xobjects;
        //    ((XGraphicsPdfRenderer)renderer).DrawXObject("/X42");
        //}

        internal void DisassociateImage()
        {
            if (_associatedImage == null)
                throw new InvalidOperationException("No image associated.");

            Dispose();
        }

        internal InternalGraphicsMode InternalGraphicsMode
        {
            get { return _internalGraphicsMode; }
            set { _internalGraphicsMode = value; }
        }
        InternalGraphicsMode _internalGraphicsMode;

        internal XImage AssociatedImage
        {
            get { return _associatedImage; }
            set { _associatedImage = value; }
        }
        XImage _associatedImage;

#if GDI
        /// <summary>
        /// Always defined System.Drawing.Graphics object. Used as 'query context' for PDF pages.
        /// </summary>
        internal Graphics _gfx;
#endif

#if WPF
        /// <summary>
        /// Always defined System.Drawing.Graphics object. Used as 'query context' for PDF pages.
        /// </summary>
#if !SILVERLIGHT
        DrawingVisual _dv;
        internal DrawingContext _dc;
#else
        internal AgDrawingContext _dc;
#endif
#endif

#if UWP
        readonly CanvasDrawingSession _cds;
#endif

        /// <summary>
        /// The transformation matrix from the XGraphics page space to the Graphics world space.
        /// (The name 'default view matrix' comes from Microsoft OS/2 Presentation Manager. I choose
        /// this name because I have no better one.)
        /// </summary>
        internal XMatrix DefaultViewMatrix;

        /// <summary>
        /// Indicates whether to send drawing operations to _gfx or _dc.
        /// </summary>
        bool _drawGraphics;

        readonly XForm _form;

#if GDI
        internal Metafile Metafile;
#endif

        /// <summary>
        /// Interface to an (optional) renderer. Currently it is the XGraphicsPdfRenderer, if defined.
        /// </summary>
        IXGraphicsRenderer _renderer;

        /// <summary>
        /// The transformation matrix from XGraphics world space to page unit space.
        /// </summary>
        XMatrix _transform;

        /// <summary>
        /// The graphics state stack.
        /// </summary>
        readonly GraphicsStateStack _gsStack;

        /// <summary>
        /// Gets the PDF page that serves as drawing surface if PDF is rendered,
        /// or null, if no such object exists.
        /// </summary>
        public PdfPage PdfPage
        {
            get
            {
                XGraphicsPdfRenderer renderer = _renderer as PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer;
                return renderer != null ? renderer._page : null;
            }
        }

#if GDI
        /// <summary>
        /// Gets the System.Drawing.Graphics objects that serves as drawing surface if no PDF is rendered,
        /// or null, if no such object exists.
        /// </summary>
        public Graphics Graphics
        {
            get { return _gfx; }
        }
#endif

        //#if CORE || GDI
        //        /// <summary>
        //        /// Critical section used to serialize access to GDI+.
        //        /// This may be necessary to use PDFsharp safely in a Web application.
        //        /// </summary>
        //        internal static readonly object GdiPlus = new object();
        //#endif

        /// <summary>
        /// Provides access to internal data structures of the XGraphics class.
        /// </summary>
        public class XGraphicsInternals
        {
            internal XGraphicsInternals(XGraphics gfx)
            {
                _gfx = gfx;
            }
            readonly XGraphics _gfx;

#if GDI
            /// <summary>
            /// Gets the underlying Graphics object.
            /// </summary>
            public Graphics Graphics
            {
                get { return _gfx._gfx; }
            }
#endif
        }

        /// <summary>
        /// (This class is under construction.)
        /// Currently used in MigraDoc
        /// </summary>
        public class SpaceTransformer
        {
            internal SpaceTransformer(XGraphics gfx)
            {
                _gfx = gfx;
            }
            readonly XGraphics _gfx;

            /// <summary>
            /// Gets the smallest rectangle in default page space units that completely encloses the specified rect
            /// in world space units.
            /// </summary>
            public XRect WorldToDefaultPage(XRect rect)
            {
                XPoint[] points = new XPoint[4];
                points[0] = new XPoint(rect.X, rect.Y);
                points[1] = new XPoint(rect.X + rect.Width, rect.Y);
                points[2] = new XPoint(rect.X, rect.Y + rect.Height);
                points[3] = new XPoint(rect.X + rect.Width, rect.Y + rect.Height);

                XMatrix matrix = _gfx.Transform;
                matrix.TransformPoints(points);

                double height = _gfx.PageSize.Height;
                points[0].Y = height - points[0].Y;
                points[1].Y = height - points[1].Y;
                points[2].Y = height - points[2].Y;
                points[3].Y = height - points[3].Y;

                double xmin = Math.Min(Math.Min(points[0].X, points[1].X), Math.Min(points[2].X, points[3].X));
                double xmax = Math.Max(Math.Max(points[0].X, points[1].X), Math.Max(points[2].X, points[3].X));
                double ymin = Math.Min(Math.Min(points[0].Y, points[1].Y), Math.Min(points[2].Y, points[3].Y));
                double ymax = Math.Max(Math.Max(points[0].Y, points[1].Y), Math.Max(points[2].Y, points[3].Y));

                return new XRect(xmin, ymin, xmax - xmin, ymax - ymin);
            }
        }
    }
}
