VisualSerializer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Print / Reach / Serialization / VisualSerializer.cs / 1 / VisualSerializer.cs

                            //------------------------------------------------------------------------------ 
//  Microsoft Avalon
//  Copyright (c) Microsoft Corporation, all rights reserved
//
//  File:       VisualSerializer.cs 
//
//  History: 
//      [....]: 10/07/2003  Created 
//      [....]: 03/02/2004  Adding HwndVisual support (DrawingVisual / EMF / Bitmap)
//      [....]: 04/13/2004  Refactor to spec //avalon/medialayer/Specifications/Visual%20Serialization.mht 
//      [....]: 12/08/2004  Code to new spec //d2/C2/DevTeam/SharedDocuments/Reach%20Visual%20to%20S0%20Serializer.mht
//
//-----------------------------------------------------------------------------
 
using System;
using System.Diagnostics; 
using System.Collections; 
using System.Collections.Specialized;
using System.Collections.Generic; 
using System.Globalization;
using System.Text;
using System.IO;
using System.Xml; 
using System.ComponentModel;
 
using System.Windows; 
using System.Windows.Automation;
using System.Windows.Media; 
using System.Windows.Media.Imaging;
using System.Windows.Xps.Packaging;
using Microsoft.Internal.AlphaFlattener;
 
using System.Security;
using System.Security.Permissions; 
 
namespace System.Windows.Xps.Serialization
{ 
    #region class VisualSerializer
    /// 
    /// Visual Serializer
    ///  
    internal class VisualSerializer: IMetroDrawingContext
    { 
        public const double PrecisionDPI = 9600; // Numeric values generated should be at least precise to that resolution 

        // XPS Specification and Reference Guide 
        // 11.2 Implementation Limits
        // Producers SHOULD produce only XPS Documents that stay within these implementation limits

        public const double PositiveLargestFloat  =  1e38; 
        public const double NegativeLargestFloat  = -1e38;
 
        public const double PositiveSmallestFloat =  2e-38; // spec says 1e-38, but it's not representable in float 
        public const double NegativeSmallestFloat = -2e-38;
 
        public const int    MaxElementCount       = 1000 * 1000;
        public const int    MaxPointCount         =  100 * 1000;
        public const int    MaxResourceCount      =   10 * 1000;
        public const int    MaxGlyphCount         =    5 * 1000; 

        public const int    MaxGradientStops      = 100; 
 
        #region Constructor
        ///  
        /// Constructor for DrawingContext which accepts the context with which this
        /// instance should be affiliated.
        /// 
        ///  XmlWriter for resource 
        ///  XmlWriter for body
        ///  
        internal VisualSerializer(System.Xml.XmlWriter resWriter, System.Xml.XmlWriter bodyWriter, PackageSerializationManager manager) 
        {
            _objects = new ArrayList(); 
            _objnams = new ArrayList();

            _resWriter  = resWriter;
            _bodyWriter = bodyWriter; 
            _manager    = manager;
 
            if (_manager != null) 
            {
                _context = new XpsTokenContext(_manager, null, null); 
            }

            _writer     = _bodyWriter;
            _tcoStack   = new Stack(); 

            SetCoordinateFormat(1); 
        } 

        #endregion Constructor 

        #region protected methods

        protected double CheckFloat(double v) 
        {
            if (v > 0) 
            { 
                if (v < PositiveSmallestFloat)
                { 
                    return 0;
                }

                if (v > PositiveLargestFloat) 
                {
                    _exceedFloatLimit = true; 
                } 
            }
            else if (v < 0) 
            {
                if (v > NegativeSmallestFloat)
                {
                    return 0; 
                }
 
                if (v < NegativeLargestFloat) 
                {
                    _exceedFloatLimit = true; 
                }
            }

            return v; 
        }
 
        void ReportLimitViolation() 
        {
            if (_exceedFloatLimit) 
            {
                _writer.WriteComment("XPSLimit:FloatRange");
                _exceedFloatLimit = false;
            } 

            if (_exceedPointLimit) 
            { 
                _writer.WriteComment("XPSLimit:PointCount");
                _exceedPointLimit = false; 
            }
        }

        protected void AppendCoordinate(StringBuilder rslt, double v) 
        {
            v = CheckFloat(v); 
 
            if (_forceGeneral > 0) // precision control ignored
            { 
                rslt.Append(v.ToString(CultureInfo.InvariantCulture));
            }
            else
            { 
                rslt.Append(v.ToString(_coordFormat, CultureInfo.InvariantCulture));
            } 
        } 

        protected void AppendPoint(StringBuilder builder, Point p, Matrix mat) 
        {
            if (!mat.IsIdentity)
            {
                p = mat.Transform(p); 
            }
 
            AppendCoordinate(builder, p.X); 
            builder.Append(',');
            AppendCoordinate(builder, p.Y); 
        }

        protected int AppendPoints(StringBuilder builder, PointCollection pc, Matrix mat)
        { 
            bool first = true;
 
            foreach (Point p in pc) 
            {
                if (first) 
                {
                    first = false;
                }
                else 
                {
                    builder.Append(' '); 
                } 

                AppendPoint(builder, p, mat); 
            }

            return pc.Count;
        } 

        protected string GetString(object obj) 
        { 
            if (obj == null)
            { 
                return null;
            }

            StringBuilder rslt = new StringBuilder(); 

            while (true) 
            { 
                DoubleCollection dc = obj as DoubleCollection;
 
                if (dc != null)
                {
                    foreach (double d in dc)
                    { 
                        if (rslt.Length != 0)
                        { 
                            rslt.Append(' '); 
                        }
 
                        rslt.Append(CheckFloat(d).ToString(CultureInfo.InvariantCulture));
                    }

                    break; 
                }
 
                PointCollection pc = obj as PointCollection; 

                if (pc != null) 
                {
                    foreach (Point p in pc)
                    {
                        if (rslt.Length != 0) 
                        {
                            rslt.Append(' '); 
                        } 

                        AppendPoint(rslt, p, Matrix.Identity); 
                    }

                    break;
                } 

                if (obj is Matrix) 
                { 
                    AppendMatrix(rslt, (Matrix)obj);
                } 
                else if (obj is Point)
                {
                    AppendPoint(rslt, (Point)obj, Matrix.Identity);
                } 
                else if (obj is double)
                { 
                    Double d = CheckFloat((double) obj); 

                    rslt.Append(d.ToString(CultureInfo.InvariantCulture)); 
                }
                else if (obj is Uri)
                {
                    rslt.Append(GetUriAsString((Uri)obj)); 
                }
                else 
                { 
                    rslt.Append(Convert.ToString(obj, CultureInfo.InvariantCulture));
                } 

                break;
            }
 
            if (rslt.Length == 0)
            { 
                return null; 
            }
            else 
            {
                return rslt.ToString();
            }
        } 

        ///  
        /// Search for cached brush 
        /// 
        protected string FindBrush(Brush brush, Rect bounds) 
        {
            Debug.Assert(!BrushProxy.IsEmpty(brush), "Should not be serializing empty brush");

            StringBuilder sbBrush; 

            _forceGeneral++; 
 
            try
            { 
                sbBrush = BrushToString(brush, bounds);
            }
            finally
            { 
                _forceGeneral--;
            } 
 
            string sBrush = sbBrush.ToString();
 
            for (int i = 0; i < _objects.Count; i++)
            {
                if ((string) _objects[i] == sBrush)
                { 
                    return _objnams[i] as string;
                } 
            } 

            _objects.Add(sBrush); 
            _objnams.Add("b" + _brushId);

            // Replace brush ID place holder with the real ID:
            //   Can't put ID in for brush reuse. 
            //   Can't replace within the whole StringBuilder because of possible appearance of bxx in GlyphRun
 
            //  
            // 
            sbBrush = sbBrush.Replace("bxx", "b" + _brushId, 0, 40); 

            _brushId++;

            _resWriter.WriteRaw(sbBrush.ToString()); 
            _resWriter.WriteWhitespace("\r\n");
 
            return _objnams[_objects.Count - 1] as string; 
        }
 

        // Output an object
        protected void WriteAttr(string attribute, object val)
        { 
            _writer.WriteAttributeString(attribute, GetString(val));
        } 
 
        // Output an object if it's not the same as default
        protected void WriteAttr(string attribute, object val, object valDefault) 
        {
            string sval = GetString(val);

            if (sval != GetString(valDefault)) 
            {
                _writer.WriteAttributeString(attribute, sval); 
            } 
        }
 
        protected string ColorToString(Color color)
        {
            string colorString = "#00FFFFFF";
 
            TypeConverter converter = _manager.GetTypeConverter(typeof(Color));
 
            if (converter != null) 
            {
                colorString = converter.ConvertTo(_context, CultureInfo.InvariantCulture, color, typeof(string)) as string; 
            }

            return colorString;
        } 

        // Convert simple brush to inline string 
        protected string SimpleBrushToString(Brush brush) 
        {
            SolidColorBrush solidBrush = brush as SolidColorBrush; 

            if (solidBrush != null) // SolidColorBrush
            {
                // Scale will normalize colors 
                Color color = Utility.Scale(solidBrush.Color, solidBrush.Opacity);
 
                return ColorToString(color); 
            }
 
            return null;
        }

        // Write GradientStopCollection 
        protected void WriteGradientStops(string prefix, GradientStopCollection gsc)
        { 
            _writer.WriteStartElement(prefix + ".GradientStops"); 

            int count = gsc.Count; 

            if (count > 0)
            {
                bool[] taken = new bool[count]; 

                // Sort gradientstops according to offsets, without changing order of stops with the same offset 
                for (int i = 0; i < count; i++) 
                {
                    int    pos = -1; 
                    double val = double.MaxValue;

                    // Find the first, free, smallest offset
                    for (int j = count - 1; j >= 0; j--) 
                    {
                        if (!taken[j]) 
                        { 
                            // treat NaN offset as MaxValue when sorting
                            double offset = gsc[j].Offset; 

                            if (double.IsNaN(offset))
                            {
                                offset = double.MaxValue; 
                            }
 
                            if (offset <= val) 
                            {
                                pos = j; 
                                val = offset;
                            }
                        }
                    } 

                    Debug.Assert(pos >= 0, "Missing offset"); 
 
                    GradientStop stop = gsc[pos];
 
                    taken[pos] = true;

                    _writer.WriteStartElement("GradientStop");
                    WriteAttr("Color", ColorToString(stop.Color)); 
                    WriteAttr("Offset", stop.Offset);
                    _writer.WriteEndElement(); 
                } 
            }
 
            _writer.WriteEndElement();

            if (count > MaxGradientStops)
            { 
                _writer.WriteComment("XPSLimit:GradientStopCount");
            } 
        } 

        private void WriteBrushHeader(string element, Brush brush) 
        {
            _writer.WriteStartElement(element);

            WriteAttr("x:Key", "bxx"); 
            WriteAttr("Opacity", brush.Opacity, 1.0);
        } 
 
        static Rect UnitRect = new Rect(0, 0, 1, 1);
 
        private void WriteTileBrush(string element, TileBrush brush, Rect bounds)
        {
            WriteBrushHeader(element, brush);
 
            BrushMappingMode mapmode = brush.ViewportUnits;
 
            if (mapmode == BrushMappingMode.Absolute) 
            {
                bounds = UnitRect; 
            }

            mapmode = BrushMappingMode.Absolute;
 
            WriteAttr("ViewportUnits", mapmode);
            WriteAttr("TileMode",      brush.TileMode); 
 
            // Remove AlignmentX/AlignmentY.
            // Or more precisely, change Viewbox/ViewPort so center alignment would replace current setting 
            double dstwidth  = brush.Viewport.Width  * bounds.Width;
            double dstheight = brush.Viewport.Height * bounds.Height;

            Rect vb = Utility.GetTileAbsoluteViewbox(brush); 

            double srcwidth  = vb.Width; 
            double srcheight = vb.Height; 

            double scalex; 
            double scaley;

            bool adjustViewport = true; // viewport
 
            switch (brush.Stretch)
            { 
                case Stretch.None: 
                    scalex = 1;
                    scaley = 1; 

                    if (srcwidth > dstwidth || srcheight > dstheight)
                    {
                        // 
                        // Fix bug 1326548: XPS serialization: Incorrect viewbox value in S0 after ImageBrush conversion
                        // 
                        // Incorrect TileBrush serialization when Stretch.None, content larger than target bounds, 
                        // and alignment specified. Need to adjust viewbox to handle alignment, not viewport.
                        // 
                        adjustViewport = false;
                    }
                    break;
 
                case Stretch.Uniform:
                    scalex = Math.Min(dstwidth / srcwidth, dstheight / srcheight); 
                    scaley = scalex; 
                    break;
 
                case Stretch.UniformToFill:
                    scalex = Math.Max(dstwidth / srcwidth, dstheight / srcheight);
                    scaley = scalex;
 
                    // UniformToFill normally maps Viewbox to an area which is larger than Viewport.
                    // So we need to adjust Viewbox to take care of alignment 
                    adjustViewport = false; // need to adjust viewbox 
                    break;
 
                case Stretch.Fill:
                default:
                    scalex = dstwidth  / srcwidth;
                    scaley = dstheight / srcheight; 
                    break;
            } 
 
            double width  = srcwidth  * scalex;
            double height = srcheight * scaley; 

            double dx, dy;

            // Calculate differeences between current alignments and the default center alignment 

            switch (brush.AlignmentX) 
            { 
                case AlignmentX.Left:
                    dx = - (dstwidth - width) / 2; 
                    break;

                case AlignmentX.Right:
                    dx = (dstwidth - width) / 2; 
                    break;
 
                case AlignmentX.Center: 
                default:
                    dx = 0; 
                    break;
            }

            switch (brush.AlignmentY) 
            {
                case AlignmentY.Top: 
                    dy = - (dstheight - height) / 2; 
                    break;
 
                case AlignmentY.Bottom:
                    dy = (dstheight - height) / 2;
                    break;
 
                case AlignmentY.Center:
                default: 
                    dy = 0; 
                    break;
            } 

            //WriteAttr("AlignmentX",   brush.AlignmentX);
            //WriteAttr("AlignmentY",   brush.AlignmentY);
            WriteAttr("ViewboxUnits", BrushMappingMode.Absolute); 

            Rect vp = brush.Viewport; 
 
            vp = new Rect(bounds.X + vp.X * bounds.Width,
                          bounds.Y + vp.Y * bounds.Height, 
                          vp.Width  * bounds.Width,
                          vp.Height * bounds.Height);

            if (adjustViewport) 
            {
                vp = new Rect(vp.Left + dx, vp.Top + dy, vp.Width, vp.Height); 
            } 
            else
            { 
                // Change direction and scale to viewbox coordinate space
                vb = new Rect(vb.Left - dx / scalex, vb.Top - dy / scaley, vb.Width, vb.Height);
            }
 
            // Adjusting Viewbox so that Stretch can be changed to "Fill"
 
            // Calculate real width/height being used in stretching 
            double w1 = vp.Width  / scalex;
            double h1 = vp.Height / scaley; 

            vb = new Rect(vb.Left + (vb.Width  - w1) / 2,   // center alignment, could be negative
                          vb.Top  + (vb.Height - h1) / 2,   // center alignment, could be negative
                          w1, 
                          h1);
 
            WriteAttr("Viewbox",  vb.ToString(CultureInfo.InvariantCulture)); 
            WriteAttr("Viewport", vp.ToString(CultureInfo.InvariantCulture));
        } 

        void SaveResetState()
        {
            // Save critical state info 
            _tcoStack.Push(_opacity);
            _tcoStack.Push(_opacityMask); 
            _tcoStack.Push(_transform); 
            _tcoStack.Push(_clip);
 
            // Reset critical state info
            _opacity     = 1;
            _opacityMask = null;
            _transform   = null; 
            _clip        = null;
        } 
 
        void RestoreState()
        { 
            // Restore critical state info
            _clip        = _tcoStack.Pop() as Geometry;
            _transform   = _tcoStack.Pop() as Transform;
            _opacityMask = _tcoStack.Pop() as Brush; 
            _opacity     = (double)_tcoStack.Pop();
        } 
 
        // Output primitives in drawing
        private void WriteDrawingBody(System.Windows.Media.Drawing drawing, Matrix worldTransform) 
        {
            SaveResetState();

            _writer.WriteStartElement("Canvas"); 

            VisualTreeFlattener vtf = new VisualTreeFlattener(this, _pageSize); 
 
            vtf.DrawingWalk(drawing, worldTransform);
 
            _writer.WriteEndElement();

            RestoreState();
        } 

        protected void WriteBitmap(string attribute, ImageSource imageSource) 
        { 
            if ((imageSource != null) && (imageSource.Height > 0) && (imageSource.Width > 0))
            { 
                string bitmapUri = null;

                TypeConverter converter = _manager.GetTypeConverter(typeof(BitmapSource));
 
                if (converter != null)
                { 
                    ColorConvertedBitmap colorConvertedBitmap = imageSource as ColorConvertedBitmap; 

                    if ( colorConvertedBitmap!=null ) 
                    {
                        if (colorConvertedBitmap.Source is FormatConvertedBitmap)
                        {
                            FormatConvertedBitmap formatConvertedBitmap = colorConvertedBitmap.Source as FormatConvertedBitmap; 
                            if (formatConvertedBitmap.Source is BitmapFrame)
                            { 
                                imageSource = formatConvertedBitmap.Source; 
                            }
                            else 
                            {
                                colorConvertedBitmap = null;
                            }
                        } 
                        else if (colorConvertedBitmap.Source is BitmapFrame)
                        { 
                            imageSource = colorConvertedBitmap.Source; 
                        }
                        else 
                        {
                            colorConvertedBitmap = null;
                        }
                    } 

                    Object obj = converter.ConvertTo(_context, null, imageSource, typeof(Uri)); 
 
                    Uri uri = obj as Uri;
 
                    if (uri != null)
                    {
                        bitmapUri = GetUriAsString(uri);
                    } 
                    else
                    { 
                        bitmapUri = obj as String; 
                    }
 
                    if (colorConvertedBitmap != null)
                    {
                        string sourceProfile = ColorTypeConverter.SerializeColorContext(_context,colorConvertedBitmap.SourceColorContext);
 
                        bitmapUri = "{ColorConvertedBitmap " + bitmapUri + " " + sourceProfile;
 
                        if (new ColorContext(PixelFormats.Default) != colorConvertedBitmap.DestinationColorContext) 
                        {
                            string destinationProfile = ColorTypeConverter.SerializeColorContext(_context, colorConvertedBitmap.DestinationColorContext); 
                            bitmapUri = bitmapUri + " " + destinationProfile;
                        }

                        bitmapUri = bitmapUri + "}"; 
                    }
                } 
 
                WriteAttr(attribute, bitmapUri);
            } 
        }

        // Convert a brush to a StringBuilder
        protected StringBuilder BrushToString(Brush brush, Rect bounds) 
        {
            StringWriter swriter = new StringWriter(CultureInfo.InvariantCulture); 
            XmlTextWriter xwriter = new XmlTextWriter(swriter); 

            xwriter.Formatting  = System.Xml.Formatting.Indented; 
            xwriter.Indentation = 4;

            XmlWriter oldwriter = _writer;
 
            _writer = xwriter;
 
            while (true) 
            {
                SolidColorBrush sb = brush as SolidColorBrush; 

                if (sb != null) // SolidColorBrush
                {
                    WriteBrushHeader("SolidColorBrush", sb); 
                    WriteAttr("Color", sb.Color);
 
                    break; 
                }
 
                LinearGradientBrush lb = brush as LinearGradientBrush;

                if (lb != null)
                { 
                    WriteBrushHeader("LinearGradientBrush", lb);
 
                    WriteTransform("Transform", lb.Transform, lb.RelativeTransform, bounds); 

                    BrushMappingMode mapmode = lb.MappingMode; 

                    if (mapmode == BrushMappingMode.Absolute)
                    {
                        bounds = UnitRect; 
                    }
 
                    mapmode = BrushMappingMode.Absolute; 

                    WriteAttr("StartPoint", Utility.MapPoint(bounds, lb.StartPoint)); 
                    WriteAttr("EndPoint", Utility.MapPoint(bounds, lb.EndPoint));
                    WriteAttr("ColorInterpolationMode", lb.ColorInterpolationMode);
                    WriteAttr("MappingMode", mapmode);
 
                    WriteAttr("SpreadMethod", lb.SpreadMethod);
 
                    WriteGradientStops("LinearGradientBrush", lb.GradientStops); 

                    break; 
                }

                RadialGradientBrush rb = brush as RadialGradientBrush;
 
                if (rb != null)
                { 
                    WriteBrushHeader("RadialGradientBrush", rb); 

                    WriteTransform("Transform", rb.Transform, rb.RelativeTransform, bounds); 

                    BrushMappingMode mapmode = rb.MappingMode;

                    if (mapmode == BrushMappingMode.Absolute) 
                    {
                        bounds = UnitRect; 
                    } 

                    mapmode = BrushMappingMode.Absolute; 

                    WriteAttr("MappingMode", mapmode);

                    WriteAttr("SpreadMethod", rb.SpreadMethod); 
                    WriteAttr("ColorInterpolationMode", rb.ColorInterpolationMode);
 
                    WriteAttr("Center", Utility.MapPoint(bounds, rb.Center)); 
                    WriteAttr("RadiusX", Math.Abs(rb.RadiusX * bounds.Width));
                    WriteAttr("RadiusY", Math.Abs(rb.RadiusY * bounds.Height)); 
                    WriteAttr("GradientOrigin", Utility.MapPoint(bounds, rb.GradientOrigin));

                    WriteGradientStops("RadialGradientBrush", rb.GradientStops);
 
                    break;
                } 
 
                ImageBrush ib = brush as ImageBrush;
 
                if (ib != null)
                {
                    WriteTileBrush("ImageBrush", ib, bounds);
 
                    WriteBitmap("ImageSource", ib.ImageSource);
 
                    WriteTransform("Transform", ib.Transform, ib.RelativeTransform, bounds); 

                    break; 
                }

                DrawingBrush db = brush as DrawingBrush;
 
                if (db != null)
                { 
                    WriteTileBrush("VisualBrush", db, bounds); 

                    Matrix trans = Utility.MergeTransform(db.Transform, db.RelativeTransform, bounds); 
                    WriteTransform("Transform", trans);

                    // Calculate approximate transformation from viewbox to world to serve as hint
                    // for bitmap effect rasterization size. 
                    Matrix drawingToWorldTransform = Utility.CreateViewboxToViewportTransform(db, bounds);
                    drawingToWorldTransform.Append(trans); 
 
                    _writer.WriteStartElement("VisualBrush.Visual");
                    WriteDrawingBody(db.Drawing, drawingToWorldTransform); 
                    _writer.WriteEndElement();

                    break;
                } 

                VisualBrush vb = brush as VisualBrush; 
 
                if (vb != null)
                { 
                    SaveResetState();

                    WriteTileBrush("VisualBrush", vb, bounds);
 
                    WriteTransform("Transform", vb.Transform, vb.RelativeTransform, bounds);
 
                    _writer.WriteStartElement("VisualBrush.Visual"); 

                    VisualTreeFlattener flattener = new VisualTreeFlattener(this, _pageSize); 

                    _writer.WriteStartElement("Canvas");

                    flattener.VisualWalk(vb.Visual); 

                    _writer.WriteEndElement(); 
 
                    _writer.WriteEndElement();
 
                    RestoreState();

                    break;
                } 

                { 
                    Debug.Assert(false, "Brush not supported"); 
                    WriteBrushHeader(brush.GetType().ToString(), brush);
                } 

                break;
            }
 
            _writer.WriteEndElement();
            _writer.Flush(); 
 
            _writer = oldwriter;
 
            return swriter.GetStringBuilder();
        }

 
#endregion
 
        #region Protected Fields 

        protected System.Xml.XmlWriter _writer; 
        protected System.Xml.XmlWriter _resWriter;
        protected System.Xml.XmlWriter _bodyWriter;

        protected int _brushId;  // = 0; 
        protected int _bitmapId; // = 0;
 
        protected PackageSerializationManager  _manager; 
        protected XpsTokenContext              _context;
 
        protected Stack     _tcoStack;     // Transform, Clip, Opacity stack

        // resource dictionary objects
        protected ArrayList _objects; 
        protected ArrayList _objnams;
 
        // common properties to apply to next element write 
        protected double    _opacity         = 1.0;
        protected Brush     _opacityMask; // = null; 
        protected Transform _transform;   // = null;
        protected Geometry  _clip;        // = null;

        protected string    _coordFormat     = "#0.##"; 
        protected Matrix    _worldTransform  = Matrix.Identity;
        protected int       _forceGeneral; //= 0; 
 
        // preserve serialization attributes
        protected String    _nameAttr;    // = null; 
        protected Visual    _node;
        protected Uri       _navigateUri;

        protected Size      _pageSize; 

        protected bool      _exceedFloatLimit; 
        protected bool      _exceedPointLimit; 
        protected int       _totalElementCount;
 
        #endregion Protected Fields

        #region Private methods
 
        // Writes a brush even if transparent.
        // Transparent brushes are used with hyperlink Paths. 
        protected void WriteBrush(string attribute, Brush brush, Rect bounds) 
        {
            if (brush != null) 
            {
                string str = SimpleBrushToString(brush);

                if (str != null) 
                {
                    _writer.WriteAttributeString(attribute, str); 
                } 
                else
                { 
                    string ob = FindBrush(brush, bounds);

                    if ((_manager != null) || (_forceGeneral >= 1) ) // to container | within resource dictionary
                    { 
                        _writer.WriteAttributeString(attribute, "{StaticResource " + ob + "}");
                    }                     // to loose files 
                    else 
                    {
                        _writer.WriteAttributeString(attribute, "{DynamicResource " + ob + "}"); 
                    }
                }
            }
        } 

        // Writes a pen even if transparent (but not if null). 
        // Transparent pen still affects rendering, since it'll shrink the fill. 
        protected void WritePen(Pen pen, Rect bounds, bool isLineGeometry)
        { 
            if (pen != null && !PenProxy.IsNull(pen))
            {
                WriteBrush("Stroke", pen.Brush, bounds);
                WriteAttr("StrokeThickness", Math.Abs(pen.Thickness)); 

                WriteAttr("StrokeStartLineCap", pen.StartLineCap, PenLineCap.Flat); 
                WriteAttr("StrokeEndLineCap", pen.EndLineCap, PenLineCap.Flat); 

                if (!isLineGeometry) 
                {
                    // not a single line segment, properties affecting line joins are relevant
                    if (pen.LineJoin == PenLineJoin.Miter)
                    { 
                        WriteAttr("StrokeMiterLimit", Math.Max(1.0, pen.MiterLimit));
                    } 
                    else 
                    {
                        WriteAttr("StrokeLineJoin", pen.LineJoin); 
                    }
                }

                if ((pen.DashStyle != null) && (pen.DashStyle.Dashes.Count != 0)) 
                {
                    WriteAttr("StrokeDashCap", pen.DashCap); 
 
                    WriteAttr("StrokeDashOffset", pen.DashStyle.Offset);
 
                    //
                    // If there are an odd number of elements in StrokeDashArray
                    // duplicate the elements.  This results in the repeating pattern
                    // demonstrated by odd elements in pen. 
                    //
                    DoubleCollection dashes = new DoubleCollection(); 
                    foreach( double d in pen.DashStyle.Dashes ) 
                    {
                        dashes.Add(Math.Abs(d)); 
                    }
                    if( pen.DashStyle.Dashes.Count%2 == 0 )
                    {
                        WriteAttr("StrokeDashArray", dashes); 
                    }
                    else 
                    { 
                        string doubleString = GetString(dashes)+" "+GetString(dashes);
                        _writer.WriteAttributeString("StrokeDashArray",doubleString ); 
                    }
                }
            }
        } 

        static private bool IsUniformScale(Matrix mat) 
        { 
            if (mat.IsIdentity)
            { 
                return true;
            }
            else
            { 
                return Utility.IsZero(mat.M11 - mat.M22) &&
                       Utility.IsZero(mat.M12) && 
                       Utility.IsZero(mat.M21); 
            }
        } 

        // convert Matrix to string
        private void AppendMatrix(StringBuilder rslt, Matrix mat)
        { 
            if (!Utility.IsIdentity(mat))
            { 
                rslt.Append(CheckFloat(mat.M11).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); 
                rslt.Append(CheckFloat(mat.M12).ToString(CultureInfo.InvariantCulture)); rslt.Append(",");
                rslt.Append(CheckFloat(mat.M21).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); 
                rslt.Append(CheckFloat(mat.M22).ToString(CultureInfo.InvariantCulture)); rslt.Append(",");
                rslt.Append(CheckFloat(mat.OffsetX).ToString(CultureInfo.InvariantCulture)); rslt.Append(",");
                rslt.Append(CheckFloat(mat.OffsetY).ToString(CultureInfo.InvariantCulture));
            } 
        }
 
        private void WritePathFigureCollection(PathFigureCollection figures, bool forFill, bool forStroke) 
        {
            int pc = 0; 

            foreach (PathFigure p in figures)
            {
                // When filling only, skip not filled figures 
                if (forFill && !forStroke && !p.IsFilled)
                { 
                    continue; 
                }
 
                _writer.WriteStartElement("PathFigure");

                pc ++;
 
                WriteAttr("StartPoint", p.StartPoint);
                WriteBool("IsClosed", p.IsClosed); 
 
                if (!p.IsFilled)
                { 
                    WriteAttr("IsFilled", "false");
                }

                PathSegmentCollection segments = p.Segments; 

                int count = segments.Count; 
 
                for (int i = 0; i < count; i ++)
                { 
                    PathSegment ps = segments[i];

                    // When stroking only, skip non-stroked segment if next segment is not either
                    if (forStroke && !forFill && !ps.IsStroked) 
                    {
                        // Non-stroked segments is still useful for providing starting point, 
                        // so they can't be totally skipped 
                        if ((i < count - 1) && !segments[i + 1].IsStroked)
                        { 
                            continue;
                        }
                    }
 
                    PolyLineSegment pl = ps as PolyLineSegment;
 
                    if (pl != null) 
                    {
                        _writer.WriteStartElement("PolyLineSegment"); 
                        WriteAttr("Points", pl.Points);
                        pc += pl.Points.Count;
                    }
                    else if (ps is PolyBezierSegment) 
                    {
                        PolyBezierSegment l = ps as PolyBezierSegment; 
 
                        _writer.WriteStartElement("PolyBezierSegment");
                        WriteAttr("Points", l.Points); 
                        pc += l.Points.Count;
                    }
                    else if (ps is LineSegment)
                    { 
                        LineSegment l = ps as LineSegment;
 
                        _writer.WriteStartElement("PolyLineSegment"); 
                        WriteAttr("Points", l.Point);
                        pc ++; 
                    }
                    else if (ps is BezierSegment)
                    {
                        BezierSegment b = ps as BezierSegment; 

                        _writer.WriteStartElement("PolyBezierSegment"); 
 
                        StringBuilder rslt = new StringBuilder();
 
                        AppendPoint(rslt, b.Point1, Matrix.Identity);
                        rslt.Append(' ');
                        AppendPoint(rslt, b.Point2, Matrix.Identity);
                        rslt.Append(' '); 
                        AppendPoint(rslt, b.Point3, Matrix.Identity);
 
                        _writer.WriteAttributeString("Points", rslt.ToString()); 
                        pc += 3;
                    } 
                    else if (ps is ArcSegment)
                    {
                        ArcSegment a = ps as ArcSegment;
 
                        if (a.Size.IsEmpty || a.Size.Width == 0 || a.Size.Height == 0)
                        { 
                            // empty size results in line segment 
                            _writer.WriteStartElement("PolyLineSegment");
                            WriteAttr("Points", a.Point); 
                            pc ++;
                        }
                        else
                        { 
                            _writer.WriteStartElement("ArcSegment");
 
                            WriteAttr("Point", a.Point); 
                            WriteAttr("Size", a.Size);
                            WriteAttr("RotationAngle", a.RotationAngle); 
                            WriteBool("IsLargeArc", a.IsLargeArc);
                            WriteAttr("SweepDirection", a.SweepDirection);
                            pc += 2;
                        } 
                    }
                    else if (ps is QuadraticBezierSegment) 
                    { 
                        QuadraticBezierSegment b = ps as QuadraticBezierSegment;
 
                        _writer.WriteStartElement("PolyQuadraticBezierSegment");

                        StringBuilder rslt = new StringBuilder();
 
                        AppendPoint(rslt, b.Point1, Matrix.Identity);
                        rslt.Append(' '); 
                        AppendPoint(rslt, b.Point2, Matrix.Identity); 

                        _writer.WriteAttributeString("Points", rslt.ToString()); 
                        pc += 2;
                    }
                    else if (ps is PolyQuadraticBezierSegment)
                    { 
                        PolyQuadraticBezierSegment b = ps as PolyQuadraticBezierSegment;
 
                        _writer.WriteStartElement("PolyQuadraticBezierSegment"); 

                        WriteAttr("Points", b.Points); 
                        pc += b.Points.Count;
                    }
                    else
                    { 
                        _writer.WriteStartElement(ps.ToString() + "PathSegment not handled");
                    } 
 
                    if (!ps.IsStroked)
                    { 
                        WriteAttr("IsStroked", "false");
                    }

                    _writer.WriteEndElement(); 
                }
 
                _writer.WriteEndElement(); 
            }
 
            if (pc > MaxPointCount)
            {
                _exceedPointLimit = true;
            } 
        }
 
        // Check if brush and/or pen actually paint anything visible 
        static private bool Visible(Brush brush, Pen pen)
        { 
            if (brush != null)
            {
                if (!BrushProxy.IsEmpty(brush))
                { 
                    return true;
                } 
            } 

            if (pen != null) 
            {
                if ((pen.Brush != null) && !BrushProxy.IsEmpty(pen.Brush))
                {
                    return true; 
                }
            } 
 
            return false;
        } 

        static private char Ord(bool b)
        {
            if (b) 
            {
                return '1'; 
            } 
            else
            { 
                return '0';
            }
        }
 
        static private char Ord(SweepDirection d)
        { 
            if (d == SweepDirection.Clockwise) 
            {
                return '1'; 
            }
            else
            {
                return '0'; 
            }
        } 
 
        // Convert a simple PathGeometry to a string which can be inlined.
        // Return null if it does not fit short-hand syntax 
        private string PathGeometryToString(PathGeometry path, Matrix map, bool forFill, bool forStroke)
        {
            if ((path.Transform != null) && !Utility.IsIdentity(path.Transform))
            { 
                map = path.Transform.Value * map;
            } 
 
            PathFigureCollection figures = path.Figures;
 
            StringBuilder rslt = new StringBuilder();

            int pc = 0;
 
            foreach (PathFigure p in figures)
            { 
                if (!p.IsFilled) 
                {
                    // Mini-language does not support IsFilled=false 
                    if (forStroke && forFill)
                    {
                        return null;
                    } 

                    // When filling only, skip not filled PathFigure 
                    if (forFill) 
                    {
                        continue; 
                    }

                    // When stroking only, ignore IsFilled flag
                } 

                PathSegmentCollection segments = p.Segments; 
 
                // Start point
                if (rslt.Length == 0) 
                {
                    if (path.FillRule == FillRule.EvenOdd)
                    {
                        // EvenOdd is the default, don't write anything 
                    }
                    else 
                    { 
                        rslt.Append("F1");
                    } 
                }

                rslt.Append('M');
 
                AppendPoint(rslt, p.StartPoint, map);
                pc ++; 
 
                // Segments
                foreach (PathSegment ps in segments) 
                {
                    if (forStroke)
                    {
                        if (!ps.IsStroked || ps.IsSmoothJoin) 
                        {
                            // Mini-language does not support IsStroked=false, IsSmoothJoin=false 
                            return null; 
                        }
                    } 

                    if (ps is PolyLineSegment)
                    {
                        PolyLineSegment l = ps as PolyLineSegment; 

                        rslt.Append('L'); 
                        pc += AppendPoints(rslt, l.Points, map); 
                    }
                    else if (ps is PolyBezierSegment) 
                    {
                        PolyBezierSegment l = ps as PolyBezierSegment;

                        rslt.Append('C'); 
                        pc += AppendPoints(rslt, l.Points, map);
                    } 
                    else if (ps is LineSegment) 
                    {
                        LineSegment l = ps as LineSegment; 

                        rslt.Append('L');
                        AppendPoint(rslt, l.Point, map);
                        pc ++; 
                    }
                    else if (ps is BezierSegment) 
                    { 
                        BezierSegment b = ps as BezierSegment;
 
                        rslt.Append('C');
                        AppendPoint(rslt, b.Point1, map);
                        rslt.Append(' ');
                        AppendPoint(rslt, b.Point2, map); 
                        rslt.Append(' ');
                        AppendPoint(rslt, b.Point3, map); 
                        pc += 3; 
                    }
                    else if (ps is ArcSegment) 
                    {
                        if (IsUniformScale(map))
                        {
                            ArcSegment a = ps as ArcSegment; 

                            Size s = a.Size; 
 
                            if (s.IsEmpty || s.Width == 0 || s.Height == 0)
                            { 
                                // empty size results in line segment
                                rslt.Append('L');
                                AppendPoint(rslt, a.Point, map);
                                pc ++; 
                            }
                            else 
                            { 
                                rslt.Append('A');
                                AppendPoint(rslt, new Point(s.Width * map.M11, s.Height * map.M22), Matrix.Identity); 
                                rslt.Append(' ');
                                rslt.Append(a.RotationAngle);
                                rslt.Append(' ');
                                rslt.Append(Ord(a.IsLargeArc)); 
                                rslt.Append(' ');
                                rslt.Append(Ord(a.SweepDirection)); 
                                rslt.Append(' '); 
                                AppendPoint(rslt, a.Point, map);
                                pc += 2; 
                            }
                        }
                        else
                        { 
                            // ArcSegment can only be transformed inline by uniform scaling + translation
                            return null; 
                        } 
                    }
                    else if (ps is QuadraticBezierSegment) 
                    {
                        QuadraticBezierSegment b = ps as QuadraticBezierSegment;

                        rslt.Append('Q'); 
                        AppendPoint(rslt, b.Point1, map);
                        rslt.Append(' '); 
                        AppendPoint(rslt, b.Point2, map); 
                        pc += 2;
                    } 
                    else if (ps is PolyQuadraticBezierSegment)
                    {
                        PolyQuadraticBezierSegment l = ps as PolyQuadraticBezierSegment;
 
                        rslt.Append('Q');
                        pc += AppendPoints(rslt, l.Points, map); 
                    } 
                    else
                    { 
                        return null;
                    }
                }
 
                // Closed?
                if (p.IsClosed) 
                { 
                    rslt.Append('Z');
                } 

            }

            if (pc > MaxPointCount) 
            {
                _exceedPointLimit = true; 
            } 

            return rslt.ToString(); 
        }

        private void WriteTransform(string attribute, Transform trans, Transform relative, Rect bounds)
        { 
            Matrix mat = Utility.MergeTransform(trans, relative, bounds);
 
            if (!Utility.IsIdentity(mat)) 
            {
                _writer.WriteAttributeString(attribute, GetString(mat)); 
            }
        }

        private void WriteTransform(string attribute, Matrix trans) 
        {
            if (!Utility.IsIdentity(trans)) 
            { 
                _writer.WriteAttributeString(attribute, GetString(trans));
            } 
        }

        private static Transform Append(Transform trans, Matrix mat)
        { 
            Matrix m = Matrix.Identity;
 
            if (trans != null) 
            {
                m = trans.Value; 
            }

            m.Append(mat);
 
            return new MatrixTransform(m);
        } 
 
        private void WriteBool(string attr, bool val)
        { 
            string str;

            if (val)
            { 
                str = "true";
            } 
            else 
            {
                str = "false"; 
            }

            _writer.WriteAttributeString(attr, str);
        } 

        private void WriteFillRule(FillRule rule) 
        { 
            string str = null;
 
            if (rule == FillRule.Nonzero)
            {
                str = "NonZero";
            } 
            else
            { 
                // EvenOdd is the default, don't write anything 
            }
 
            if (str != null)
            {
                _writer.WriteAttributeString("FillRule", str);
            } 
        }
 
        internal bool WriteGeometry(string element, string attribute, Geometry geo, Matrix map, bool asElement, bool forFill, bool forStroke) 
        {
            Debug.Assert(forFill || forStroke, "Either forFill or forStoke should be true"); 

            PathGeometry     pg = null;
            string           p = null;
 
            pg = Utility.GetAsPathGeometry(geo);
 
            if (IsPathGeometryEmpty(pg, forFill, forStroke)) 
            {
                return false; 
            }

            if (attribute != null)
            { 
                if (!asElement && (pg != null))
                { 
                    p = PathGeometryToString(pg, map, forFill, forStroke); 

                    // If it can be converted to a string, output as attribute 
                    if (p != null)
                    {
                        _writer.WriteAttributeString(attribute, p);
 
                        return false;
                    } 
                } 

                // Output as element 
                _writer.WriteStartElement(element + "." + attribute);
            }

            _writer.WriteStartElement("PathGeometry"); 

            Transform trans = Append(pg.Transform, map); 
 
            WriteFillRule(pg.FillRule);
 
            p = PathGeometryToString(pg, trans.Value, forFill, forStroke);

            if (p != null)
            { 
                // Remove "Fn " prefix
                _writer.WriteAttributeString("Figures", p.Substring(3)); 
            } 
            else
            { 
                PushCoordinateScope(trans);

                WriteTransform("Transform", trans, null, Rect.Empty);
 
                WritePathFigureCollection(pg.Figures, forFill, forStroke);
 
                PopCoordinateScope(); 
            }
 
            _writer.WriteEndElement();

            if (attribute != null)
            { 
                _writer.WriteEndElement();
            } 
 
            return true;
        } 

        private static bool IsPathFigureEmpty(PathFigureCollection figures, bool forFill, bool forStroke)
        {
            if (figures == null) 
            {
                return true; 
            } 

            foreach (PathFigure p in figures) 
            {
                // When filling only, skip not filled PathFigure
                if (forFill && !forStroke && !p.IsFilled)
                { 
                    continue;
                } 
 
                PathSegmentCollection segments = p.Segments;
 
                if (segments != null)
                {
                    foreach (PathSegment ps in segments)
                    { 
                        // When stroking only, skip not stroked PathSegment
                        if (forStroke && !forFill && !ps.IsStroked) 
                        { 
                            continue;
                        } 

                        // Found something real
                        return false;
                    } 
                }
            } 
 
            return true;
        } 

        /// 
        /// Check geometry for emptyness before generating it. Empty geometry not allowed in Metro
        ///  
        /// 
        ///  
        ///  
        /// 
        private static bool IsPathGeometryEmpty(PathGeometry pg, bool forFill, bool forStroke) 
        {
            if ((pg == null) || pg.Bounds.IsEmpty)
            {
                return true; 
            }
 
            if (!forStroke && !Utility.IsRenderVisible(pg.Bounds)) 
            {
                // When not stroking, geometry with zero-area bounds displays nothing, otherwise 
                // the stroke may widen geometry to display something.
                return true;
            }
 
            return IsPathFigureEmpty(pg.Figures, forFill, forStroke);
        } 
 
        /// 
        /// Returns true if input is valid in an XML file, according to specification at 
        /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets.
        /// 
        /// input character
        private static bool IsXmlValidChar(char c) 
        {
            if ( 
                c == 0x09 || c == 0x0A || c == 0x0D || 
                (c >= 0x20 && c <= 0xD7FF) ||
                (c >= 0xE000 && c <= 0xFFFD) 
                )
            {
                // valid
                return true; 
            }
            else 
            { 
                return false;
            } 
        }

        /// 
        /// Filters invalid XML characters from GlyphRun by replacing with spaces. 
        /// 
        ///  
        /// Returns the filtered GlyphRun if modified, otherwise the original GlyphRun 
        private static GlyphRun FilterXmlInvalidChar(GlyphRun glyphRun)
        { 
            if (glyphRun.Characters == null)
            {
                // no characters to worry about
                return glyphRun; 
            }
 
            // 
            // Fix bug 1334838: XPS Serialization: Incorrect XML characters not filtered out of S0 markup
            // 
            // We temporarily fix by replacing invalid characters with spaces. The advance widths aren't
            // touched, and so visual appearance should remain the same except for missing glyphs
            // corresponding to replaced characters.
            // 
            List filteredCharacters = null;
 
            for (int i = 0; i < glyphRun.Characters.Count; i++) 
            {
                if (!IsXmlValidChar(glyphRun.Characters[i])) 
                {
                    if (filteredCharacters == null)
                    {
                        filteredCharacters = new List(glyphRun.Characters); 
                    }
 
                    // replace invalid character with space 
                    filteredCharacters[i] = ' ';
                } 
            }

            if (filteredCharacters == null)
            { 
                // no filtering needed
                return glyphRun; 
            } 
            else
            { 
                // return filtered GlyphRun
                GlyphRun filtered = new GlyphRun(
                    glyphRun.GlyphTypeface,
                    glyphRun.BidiLevel, 
                    glyphRun.IsSideways,
                    glyphRun.FontRenderingEmSize, 
                    glyphRun.GlyphIndices, 
                    glyphRun.BaselineOrigin,
                    glyphRun.AdvanceWidths, 
                    glyphRun.GlyphOffsets,
                    filteredCharacters,
                    glyphRun.DeviceFontName,
                    glyphRun.ClusterMap, 
                    glyphRun.CaretStops,
                    glyphRun.Language 
                    ); 

                return filtered; 
            }
        }

        ///  
        /// Gets URI as string, which may be relative if pointing within same XPS document.
        ///  
        ///  
        /// 
        private string GetUriAsString(Uri uri) 
        {
            return uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped);
        }
 
        /// 
        /// Determines if common attributes need to be preserved even if the element is transparent. 
        ///  
        /// 
        /// Attributes that need to be preserved even if the element is transparent: 
        /// - Name: May be used as link target.
        /// - NavigateUri: Hyperlink clickable area.
        /// 
        private bool PreserveTransparent() 
        {
            return _nameAttr != null || _navigateUri != null; 
        } 

        ///  
        /// Writes attributes common to Canvas/Path/Glyphs.
        /// 
        /// Also write automation properties. Glyphs does not have automation properties.
        private void WriteCommonAttrs(bool bWriteAutomation) 
        {
            if (_nameAttr != null) 
            { 
                Debug.Assert(_nameAttr.Length > 0, "Empty _nameAttr");
 
                WriteAttr("Name", _nameAttr);
                _nameAttr = null;
            }
 
            if (_node != null)
            { 
                if( bWriteAutomation ) 
                {
                    string apName = AutomationProperties.GetName(_node); 

                    if (! String.IsNullOrEmpty(apName))
                    {
                        WriteAttr("AutomationProperties.Name", apName); 
                    }
 
                    string apHelpText = AutomationProperties.GetHelpText(_node); 

                    if (! String.IsNullOrEmpty(apHelpText)) 
                    {
                        WriteAttr("AutomationProperties.HelpText", apHelpText);
                    }
                } 

                _node = null; 
            } 

            if (_navigateUri != null) 
            {
                WriteAttr("FixedPage.NavigateUri", _navigateUri);
                _navigateUri = null;
            } 
        }
 
        #endregion 

        #region IMetroDrawingContext virtual methods override 

        // Check for relative brush
        static public bool NeedBounds(Brush b)
        { 
            if (b == null)
            { 
                return false; 
            }
 
            TileBrush tb = b as TileBrush;

            if (tb != null)
            { 
                return (tb is VisualBrush) || (tb.ViewportUnits == BrushMappingMode.RelativeToBoundingBox);
            } 
 
            GradientBrush gb = b as GradientBrush;
 
            if (gb != null)
            {
                return gb.MappingMode == BrushMappingMode.RelativeToBoundingBox;
            } 

            return false; 
        } 

#if INVERSE_PATH_FOR_BRUSH_SHARING 

        // Get inverse transfrom from bounding rectangle, adjust RenderTransform accordingly
        // so that brushes can be independent of bounding boxes
        Matrix GetBoundsInverse(Rect bounds) 
        {
            Matrix mat = Matrix.Identity; 
 
            mat.Scale(bounds.Width, bounds.Height);
            mat.Translate(bounds.Left, bounds.Top); 

            if (_transform != null)
            {
                mat.Append(_transform.Value); 
            }
 
            _transform = new MatrixTransform(mat); 

            Matrix inverse = Matrix.Identity; 

            inverse.Translate(-bounds.Left, -bounds.Top);
            inverse.Scale(1 / bounds.Width, 1 / bounds.Height);
 
            return inverse;
        } 
 
#endif
 
#if SPLITTING_PATH

        /// 
        /// Check if a geometry is different under filling and stroking, check for IsFilled=false and IsStroked=false 
        /// 
        ///  
        ///  
        internal static bool IsGeometryPolymophic(Geometry geo)
        { 
            PathGeometry pg = geo as PathGeometry;

            if (pg != null)
            { 
                PathFigureCollection figures = pg.Figures;
 
                if (figures != null) 
                {
                    foreach (PathFigure p in figures) 
                    {
                        if (!p.IsFilled)
                        {
                            return true; 
                        }
 
                        PathSegmentCollection segments = p.Segments; 

                        if (segments != null) 
                        {
                            foreach (PathSegment ps in segments)
                            {
                                if (!ps.IsStroked) 
                                {
                                    return true; 
                                } 
                            }
                        } 
                    }
                }

                return false; 
            }
 
            CombinedGeometry cg = geo as CombinedGeometry; 

            if (cg != null) 
            {
                return IsGeometryPolymophic(cg.Geometry1)  ||
                       IsGeometryPolymophic(cg.Geometry2);
            } 

            GeometryGroup gg = geo as GeometryGroup; 
 
            if (gg != null)
            { 
                if (gg.Children != null)
                {
                    foreach (Geometry g in gg.Children)
                    { 
                        if (IsGeometryPolymophic(g))
                        { 
                            return true; 
                        }
                    } 
                }
            }

            return false; 
        }
 
#endif 

        ///  
        /// Draw a Geometry with the provided Brush and/or Pen.
        /// If both the Brush and Pen are null this call is a no-op.
        /// 
        void IMetroDrawingContext.DrawGeometry(Brush brush, Pen pen, Geometry geometry) 
        {
            if (geometry == null) 
            { 
                return;
            } 

            bool forFill = Visible(brush, null);
            bool forStroke = Visible(null, pen);
 
            if (PreserveTransparent())
            { 
                // 
                // Preserve Path element even if transparent brush/pen, since attributes
                // attached to this Path need preservation, i.e. hyperlink. 
                //
                // If null fill, change to transparent fill to ensure hyperlink is clickable.
                //
                if (brush == null) 
                {
                    forFill = true; // have WriteGeometry treat as fill even though it isn't visible 
                    brush = Brushes.Transparent; 
                }
            } 
            else
            {
                if (!Visible(brush, pen))
                    return; 
            }
 
#if SPLITTING_PATH 

            if ((brush != null) && (pen != null) && IsGeometryPolymophic(geometry)) 
            {
                ((IMetroDrawingContext) this).DrawGeometry(brush, null, geometry);

                brush = null; 
            }
#endif 
 
            Rect bounds = geometry.GetRenderBounds(pen);
 
            if (!Utility.IsRenderVisible(bounds))
            {
                return;
            } 

            _tcoStack.Push(_transform); 
 
            Matrix inverse = Matrix.Identity;
 
#if INVERSE_PATH_FOR_BRUSH_SHARING
            // To generate more sharable relative brushes, it's possible to use a unit bounding box
            // and apply a reverse transformation to geometry.
            // This can generate strange viewbox and complicated path. 
            // Disable for the memoent
            if ((pen == null) && (_clip == null) && (_opacityMask == null) && 
                !(geometry is CombinedGeometry) && NeedBounds(brush)) 
            {
                inverse = GetBoundsInverse(bounds); 
                bounds  = UnitRect;
            }
#endif
 
            _writer.WriteStartElement("Path");
 
            WriteCommonAttrs(true); 

            bool isLineGeometry = Utility.IsLineSegment(geometry); 
            WritePen(pen, bounds, isLineGeometry);

            WriteBrush("Fill", brush, geometry.Bounds);
 
            bool asElement = WriteTCO("Path", _transform, _clip, Matrix.Identity, _opacity, _opacityMask, bounds);
 
            WriteGeometry("Path", "Data", geometry, inverse, asElement, forFill, forStroke); 

            _writer.WriteEndElement(); 

            _transform = _tcoStack.Pop() as Transform;

            _totalElementCount ++; 
            ReportLimitViolation();
        } 
 
        /// 
        /// Draw an Image into the region specified by the Rect, which may be animate. 
        /// The Image will potentially be stretched and distorted to fit the Rect.
        /// For more fine grained control, consider filling a Rect with an ImageBrush via DrawRect.
        /// 
        void IMetroDrawingContext.DrawImage(ImageSource image, Rect rectangle) 
        {
            if (image != null) 
            { 
                Brush brush = new ImageBrush((BitmapSource)image);
 
                ((IMetroDrawingContext)this).DrawGeometry(brush, null, new RectangleGeometry(rectangle));
            }
            else if (PreserveTransparent())
            { 
                // Transparent image, but element has attributes that need preservation (i.e. Name or NavigateUri).
                ((IMetroDrawingContext)this).DrawGeometry(null, null, new RectangleGeometry(rectangle)); 
            } 
        }
 
        /// 
        /// For translation only transform, extract out translation
        /// 
        static private Transform ExtractTranslation(Transform trans, out double dx, out double dy) 
        {
            dx = 0; 
            dy = 0; 

            if (!Utility.IsIdentity(trans)) 
            {
                Matrix mat = trans.Value;

                if (Utility.IsOne(mat.M11) && 
                    Utility.IsOne(mat.M22) &&
                    Utility.IsZero(mat.M12) && 
                    Utility.IsZero(mat.M21)) 
                {
                    dx = mat.OffsetX; 
                    dy = mat.OffsetY;

                    return Transform.Identity;
                } 
            }
 
            return trans; 
        }
        ///  
        /// Draw a GlyphRunAsImage.
        /// 
        /// 
        /// This is used if GlyphRun cannot be embedded in XPS; therefore rasterization is needed. 
        /// 
        internal 
        void 
        DrawGlyphRunAsImage(Brush foreground, GlyphRun glyphRun)
        { 
            Toolbox.StartEvent(Toolbox.DRXRASTERGUID);

            // rasterize GlyphRunDrawing to bitmap
            GlyphRunDrawing drawing = new GlyphRunDrawing(foreground, glyphRun); 

            Matrix bitmapToDrawingTransform; 
 
            BitmapSource bitmap = Utility.RasterizeDrawing(
                drawing, 
                drawing.Bounds,
                _worldTransform,
                out bitmapToDrawingTransform
                ); 

            if (bitmap != null) 
            { 
                //
                // Draw the rasterized glyphs. bitmapToDrawingTransform should strictly 
                // be a translation/scaling transform, which is why we can use Rect.Transform and
                // avoid a Push/Pop.
                //
                Debug.Assert(Utility.IsScaleTranslate(bitmapToDrawingTransform)); 

                Rect rect = new Rect(0, 0, bitmap.Width, bitmap.Height); 
                rect.Transform(bitmapToDrawingTransform); 

                ((IMetroDrawingContext)this).DrawImage(bitmap, rect); 
            }

            Toolbox.EndEvent(Toolbox.DRXRASTERGUID);
        } 

 
        /// 
        /// Critical    - It calls critical internal function CriticalFileReadPermission
        /// 
        [SecurityCritical]
        private static bool EmbeddingAllowed(GlyphTypeface typeface)
        {
            CodeAccessPermission fontReadPermission = typeface.CriticalFileReadPermission; 

            FontEmbeddingRight embeddingRights = FontEmbeddingRight.Installable; 
 

            if (fontReadPermission != null) 
            {
                fontReadPermission.Assert(); // Blessed assert
            }
 
            try
            { 
                embeddingRights = typeface.EmbeddingRights; 
            }
            finally 
            {
                if (fontReadPermission != null)
                {
                    CodeAccessPermission.RevertAssert(); 
                }
            } 
 
            return (XpsFontSubsetter.DetermineEmbeddingAction(embeddingRights) != FontEmbeddingAction.ImageOnlyFont);
        } 

        /// 
        /// Draw a GlyphRun.
        ///  
        ///
        /// Critical    - It uses internal permission to get FontUri 
        /// TreatAsSafe - The Uri is used for font subseeting, and font is written to container for controoled printing 
        ///
        [SecurityCritical, SecurityTreatAsSafe] 
        void IMetroDrawingContext.DrawGlyphRun(Brush foreground, GlyphRun glyphRun)
        {
            if (glyphRun == null)
            { 
                return;
            } 
 
            if (PreserveTransparent())
            { 
                if (foreground == null)
                {
                    // Give transparent foreground to ensure hyperlink is clickable.
                    foreground = Brushes.Transparent; 
                }
            } 
            else 
            {
                if (!Visible(foreground, null)) 
                    return;
            }

            if (!EmbeddingAllowed(glyphRun.GlyphTypeface)) 
            {
                this.DrawGlyphRunAsImage(foreground, glyphRun); 
                return; 
            }
 
            _writer.WriteStartElement("Glyphs");

            double dx = 0, dy = 0;
 
            Transform trans = _transform;
            Matrix clipMat = Matrix.Identity; 
 
            // Optimization: apply translation to OriginX/OriginY if possible
            if ((foreground is SolidColorBrush) && (_opacityMask == null)) 
            {
                trans = ExtractTranslation(_transform, out dx, out dy);

                if (_clip != null) 
                {
                    clipMat = new Matrix(1, 0, 0, 1, dx, dy); 
                } 
            }
 
            WriteCommonAttrs(false);

            WriteAttr("OriginX", glyphRun.BaselineOrigin.X + dx);
            WriteAttr("OriginY", glyphRun.BaselineOrigin.Y + dy); 
            WriteAttr("FontRenderingEmSize", glyphRun.FontRenderingEmSize);
 
            Uri uri = Utility.GetFontUri(glyphRun.GlyphTypeface); 

            if (_manager != null) 
            {
                TypeConverter converter = _manager.GetTypeConverter(typeof(GlyphRun));

                if (converter != null) 
                {
                    uri = converter.ConvertTo(_context, null, glyphRun, typeof(Uri)) as Uri; 
                } 
            }
 
            WriteAttr("FontUri", uri);
            WriteAttr("StyleSimulations", glyphRun.GlyphTypeface.StyleSimulations, StyleSimulations.None);

            // BidiLevel must be in range [0, 61] 
            int bidiLevel = glyphRun.BidiLevel;
            if (bidiLevel > 61) 
            { 
                bidiLevel = 61;
            } 
            else if (bidiLevel < 0)
            {
                bidiLevel = 0;
            } 

            WriteAttr("BidiLevel", bidiLevel, 0); 
 
            if (glyphRun.IsSideways)
            { 
                WriteBool("IsSideways", glyphRun.IsSideways);
            }

            // 
            // Fix bug 1334838: XPS Serialization: Incorrect XML characters not filtered out of S0 markup
            // 
            // Filter XML-invalid characters from GlyphRun, returning the filtered GlyphRun. The filtering 
            // affects only UnicodeString.
            // 
            GlyphRun serializeGlyphRun = FilterXmlInvalidChar(glyphRun);

            // serialize complex properties, running full markup size optimizations
            // per [....]/text/DesignDocsAndSpecs/Glyphs%20element%20and%20GlyphRun%20object.htm#optimizing 
            GlyphsSerializer glyphsSerializer = new GlyphsSerializer(serializeGlyphRun);
            string characters, indices, caretStops; 
            glyphsSerializer.ComputeContentStrings(out characters, out indices, out caretStops); 

            bool exceedGlyphCount = false; 

            if (!String.IsNullOrEmpty(characters))
            {
                // Leading '{' in a string is used for markup extension. 
                // Prefix it with '{}' to avoid being confused with markup extension
                if (characters[0] == '{') 
                { 
                    characters = "{}" + characters;
                } 

                if (serializeGlyphRun.Characters.Count > MaxGlyphCount)
                {
                    exceedGlyphCount = true; 
                }
 
                WriteAttr("UnicodeString", characters); 
            }
 
            if (!String.IsNullOrEmpty(indices))
            {
                if (serializeGlyphRun.GlyphIndices.Count > MaxGlyphCount)
                { 
                    exceedGlyphCount = true;
                } 
 
                WriteAttr("Indices", indices);
            } 

            if (!String.IsNullOrEmpty(caretStops))
            {
                WriteAttr("CaretStops", caretStops); 
            }
 
            Rect bounds = UnitRect; 

            if (NeedBounds(_opacityMask) || NeedBounds(foreground)) 
            {
                bounds = glyphRun.ComputeInkBoundingBox();

                bounds.X += glyphRun.BaselineOrigin.X + dx; 
                bounds.Y += glyphRun.BaselineOrigin.Y + dy;
            } 
 
            WriteBrush("Fill", foreground, bounds);
 
            if (glyphRun.Language != null &&
                glyphRun.Language != _manager.Language)
            {
                // Only write language attribute if it doesn't match the fixedpage language. 
                // WriteTCO might generate an element, so must write this attribute before.
                WriteAttr(XpsS0Markup.XmlLang, glyphRun.Language.ToString()); 
            } 

            WriteTCO("Glyphs", trans, _clip, clipMat, _opacity, _opacityMask, bounds); 

            _writer.WriteEndElement();

            if (exceedGlyphCount) 
            {
                _writer.WriteComment("XPSLimit:GlyphCount"); 
            } 

            _totalElementCount ++; 
            ReportLimitViolation();
        }

        ///  
        /// Pop the most recent Push operation, which may have been a Clip, Opacity, Transform, etc.
        ///  
        void IMetroDrawingContext.Pop() 
        {
            _transform   = null; 
            _clip        = null;
            _opacity     = 1.0;
            _opacityMask = null;
 
            int level = (int)_tcoStack.Pop();
 
            PopCoordinateScope(); 

            while (level > 0) 
            {
                _writer.WriteEndElement();
                level --;
            } 

            if (_tcoStack.Count == 0) // end of page 
            { 
                if (_brushId > MaxResourceCount)
                { 
                    _writer.WriteComment("XPSLimit:ResourceCount");
                }

                if (_totalElementCount > MaxElementCount) 
                {
                    _writer.WriteComment("XPSLimit:ElementCount"); 
                } 
            }
        } 

        void IMetroDrawingContext.Comment(String str)
        {
            _writer.WriteComment(str); 
        }
 
        bool WriteTCO(string element, Transform transform, Geometry clip, Matrix clipMat, double opacity, Brush opacityMask, Rect bounds) 
        {
            // Extract opacity from SolidColorBrush OpacityMask 
            if (opacityMask != null)
            {
                SolidColorBrush sb = opacityMask as SolidColorBrush;
 
                if (sb != null)
                { 
                    opacity *= Utility.NormalizeOpacity(sb.Color.ScA) * Utility.NormalizeOpacity(opacityMask.Opacity); 
                    opacityMask = null;
                } 
            }

            if (!Utility.IsOne(opacity))
            { 
                WriteAttr("Opacity", Math.Min(Math.Max( opacity, 0.0),1.0));
            } 
 
            if (opacityMask != null && !BrushProxy.IsEmpty(opacityMask))
            { 
                WriteBrush("OpacityMask", opacityMask, bounds);
            }

            WriteTransform("RenderTransform", transform, null, Rect.Empty); 

            bool asElement = false; 
 
            if (clip != null)
            { 
                asElement = WriteGeometry(element, "Clip", clip, clipMat, false, true, false);
            }

            return asElement; 
        }
 
        void SetCoordinateFormat(double scale) 
        {
            scale = scale * PrecisionDPI / 9600; 

            if (scale > 1000)
            {
                _coordFormat = "G";        // 15 digits 
            }
            else if (scale > 100) 
            { 
                _coordFormat = "#0.#####"; //  5 digits
            } 
            else if (scale > 10)
            {
                _coordFormat = "#0.####";  //  4 digits
            } 
            else if (scale > 1)
            { 
                _coordFormat = "#0.###";   //  3 digits 
            }
            else if (scale > 0.1) 
            {
                _coordFormat = "#0.##";    //  2 digits
            }
            else 
            {
                _coordFormat = "#0.#";     //  1 digit 
            } 
        }
 
        void PushCoordinateScope(Transform transform)
        {
            _tcoStack.Push(_worldTransform);
            _tcoStack.Push(_coordFormat); 

            if (!Utility.IsIdentity(transform)) 
            { 
                Matrix mat = transform.Value;
 
                mat.Append(_worldTransform);

                _worldTransform = mat;
 
                SetCoordinateFormat(Math.Min(Utility.GetScaleX(mat), Utility.GetScaleY(mat)));
            } 
        } 

        void PopCoordinateScope() 
        {
            _coordFormat    = _tcoStack.Pop() as string;
            _worldTransform = (Matrix)_tcoStack.Pop();
        } 

        void IMetroDrawingContext.Push( 
            Matrix mat, 
            Geometry clip,
            double opacity, 
            Brush opacityMask,
            Rect maskBounds,
            bool onePrimitive,
 
            // serialization attributes
            String nameAttr, 
            Visual node, 
            Uri navigateUri,
            EdgeMode edgeMode 
            )
        {
            Debug.Assert(nameAttr == null || nameAttr.Length > 0, "Bad name attribute");
 
            Transform transform = new MatrixTransform(mat);
 
            PushCoordinateScope(transform); 

            bool noTrans = Utility.IsIdentity(transform); 

            if (node != null)
            {
                _node = node; 
            }
 
            int elementLevels = 0; 

            if ((clip == null) && 
                noTrans &&
                Utility.IsOne(opacity) &&
                (opacityMask == null) &&
                nameAttr == null && 
                navigateUri == null &&
                edgeMode == EdgeMode.Unspecified) 
            { 
                // If there is no clip, transform and opacity, nothing to generate
            } 
            else if (onePrimitive &&
                edgeMode == EdgeMode.Unspecified)   // EdgeMode only valid on Canvas
            {
                _transform   = transform; 
                _clip        = clip;
                _opacity     = opacity; 
                _opacityMask = opacityMask; 

                if (nameAttr != null) 
                {
                    Debug.Assert(_nameAttr == null, "Empty");
                    _nameAttr = nameAttr;
                } 

                if (navigateUri != null) 
                { 
                    _navigateUri = navigateUri;
                } 
            }
            else
            {
                _writer.WriteStartElement("Canvas"); 

                // write common attributes 
                _nameAttr = nameAttr; 
                _navigateUri = navigateUri;
 
                WriteCommonAttrs(true);

                // write RenderOptions.EdgeMode
                if (edgeMode != EdgeMode.Unspecified) 
                {
                    WriteAttr("RenderOptions.EdgeMode", edgeMode); 
                } 

                // write transform, clip, opacity 
                WriteTCO("Canvas", transform, clip, Matrix.Identity, opacity, opacityMask, maskBounds);
                elementLevels = 1;
            }
 
            // push number of element levels introduced by this IMetroDrawingContext.Push
            _tcoStack.Push(elementLevels); 
        } 

        #endregion Public Methods 

    }
    #endregion
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK