File: Serialization\Manager\ReachSerializationUtils.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\ReachFramework\ReachFramework.csproj (ReachFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Xml;
using System.IO;
using System.IO.Packaging;
using System.Security;
using System.ComponentModel.Design.Serialization;
using System.Windows.Xps.Packaging;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Markup;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Printing;
 
using MS.Utility;
 
namespace System.Windows.Xps.Serialization
{
    internal struct DependencyPropertyList
    {
        public
        DependencyPropertyList(
            int capacity
            )
        {
            List = new System.Windows.DependencyProperty[capacity];
            Count = 0;
        }
 
 
        //
        // Non-lock-required Read methods
        // (Always safe to call when locking "safe" write operations are used)
        //
 
        public System.Windows.DependencyProperty[] List;
        public int Count;
 
        public
        void
        EnsureIndex(
            int index
            )
        {
            int delta = (index + 1) - Count;
            if (delta > 0)
            {
                Add(delta);
            }
        }
 
        public
        bool
        IsValidIndex(
            int index
            )
        {
            return (index < Count);
        }
 
        public
        int
        IndexOf(
            System.Windows.DependencyProperty value
            )
        {
            int index = -1;
 
            for (int i = 0; i < Count; i++)
            {
                if (List[i].Equals(value))
                {
                    index = i;
                    break;
                }
            }
 
            return index;
        }
 
        public
        bool
        Contains(
            System.Windows.DependencyProperty value
            )
        {
            return (IndexOf(value) != -1);
        }
 
 
        //
        // Lock-required Write operations
        // "Safe" methods for Reader lock-free operation
        //
 
        //
        // Increase size by one, new value is provided
        //
        public
        void
        Add(
            System.Windows.DependencyProperty item
            )
        {
            // Add without Count adjustment (incr Count after valid item added)
            int index = Add(1, false);
            List[index] = item;
            Count++;
        }
 
        //
        // Increase size by one, new value is provided
        //
        public
        void
        Add(
            ref System.Windows.DependencyProperty item
            )
        {
            // Add without Count adjustment (incr Count after valid item added)
            int index = Add(1, false);
            List[index] = item;
            Count++;
        }
 
        //
        // Increase size by one, new value is default value
        //
        public
        int
        Add(
            )
        {
            return Add(1, true);
        }
 
        //
        // Increase size of array by delta, fill with default values
        //
        public
        int
        Add(
            int delta
            )
        {
            return Add(delta, true);
        }
 
        //
        // Increase size of array by delta, fill with default values
        // Allow disable of automatic Count increment so that cases where
        // non-default values are to be added to the list can be done before
        // count is changed. This is important for non-locking scenarios
        // (i.e. count is adjusted after array size changes)
        //
        private
        int
        Add(
            int delta,
            bool incrCount
            )
        {
            if (List != null)
            {
                if ((Count + delta) > List.Length)
                {
                    System.Windows.DependencyProperty[] newList = new System.Windows.DependencyProperty[Math.Max(List.Length * 2, Count + delta)];
                    List.CopyTo(newList, 0);
                    List = newList;
                }
            }
            else
            {
                List = new System.Windows.DependencyProperty[Math.Max(delta, 2)];
            }
 
            // New arrays auto-initialized to default entry values
            // Any resued entried have already been cleared out by Remove or Clear
 
            int index = Count;
 
            // Optional adjustment of Count
            if (incrCount)
            {
                // Adjust count after resize so that array bounds and data
                // are never invalid (good for locking writes without synchronized reads)
                Count += delta;
            }
 
            return index;
        }
 
        public
        void
        Sort(
            )
        {
            if (List != null)
            {
                Array.Sort(List, 0, Count);
            }
        }
 
        public
        void
        AppendTo(
            ref DependencyPropertyList destinationList
            )
        {
            for (int i = 0; i < Count; i++)
            {
                destinationList.Add(ref List[i]);
            }
        }
 
        public
        System.Windows.DependencyProperty[]
        ToArray(
            )
        {
            System.Windows.DependencyProperty[] array = new System.Windows.DependencyProperty[Count];
            Array.Copy(List, 0, array, 0, Count);
 
            return array;
        }
 
 
        //
        // Lock-required Write operations
        // "UNSafe" methods for Reader lock-free operation
        //
        // If any of these methods are called, the entire class is considered
        // unsafe for Reader lock-free operation for that point on (meaning
        // Reader locks must be taken)
        //
 
        public
        void
        Clear(
            )
        {
            // Return now unused entries back to default
            Array.Clear(List, 0, Count);
 
            Count = 0;
        }
 
        public
        void
        Remove(
            System.Windows.DependencyProperty value
            )
        {
            int index = IndexOf(value);
            if (index != -1)
            {
                // Shift entries down
                Array.Copy(List, index + 1, List, index, (Count - index - 1));
 
                // Return now unused entries back to default
                Array.Clear(List, Count - 1, 1);
 
                Count--;
            }
        }
    };
 
    internal class ReachHierarchySimulator
    {
 
        #region Constructor
 
        public
        ReachHierarchySimulator(
            PackageSerializationManager   manager,
            Object                        serializedObject
            )
        {
            ArgumentNullException.ThrowIfNull(manager);
            ArgumentNullException.ThrowIfNull(serializedObject);
 
            this._serializationManager      = manager;
            this._serializedObject          = serializedObject;
            this._documentSequenceXmlWriter = null;
            this._documentXmlWriter         = null;
            this._pageXmlWriter             = null;
        }
 
        #endregion Constructor
 
        #region Internal Methods
 
        internal
        void
        BeginConfirmToXPSStructure(
            bool mode
            )
        {
            //
            // In case we have a FixedPage or a Visual as a root, we have to simulate
            // a containing FixedDocument-FixedPage ... etc.
            //
            if((_serializedObject.GetType() == typeof(System.Windows.Documents.FixedDocument)) ||
               (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(_serializedObject.GetType())
                && (_serializedObject.GetType() != typeof(System.Windows.Documents.FixedDocumentSequence))))
            {
                //
                // Build the necessary wrapper for a FixedDocument
                //
                _documentSequenceXmlWriter = SimulateBeginFixedDocumentSequence();
            }
            else if(_serializedObject.GetType() == typeof(System.Windows.Documents.FixedPage))
            {
                //
                // Build the necessary wrapper for a FixedPage
                //
                _documentSequenceXmlWriter = SimulateBeginFixedDocumentSequence();
                _documentXmlWriter         = SimulateBeginFixedDocument();
                XpsSerializationPrintTicketRequiredEventArgs e =
                    new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedPagePrintTicket,
                                                             0);
                ((IXpsSerializationManager)_serializationManager).OnXPSSerializationPrintTicketRequired(e);
                XpsOMSerializationManager xpsOMSerializationManager = _serializationManager as XpsOMSerializationManager;
 
                PrintTicket printTicket = null;
                if( e.Modified )
                {
                    printTicket =  e.PrintTicket;
                }
                Toolbox.Layout(_serializedObject as FixedPage, printTicket);
 
                ((IXpsSerializationManager)_serializationManager).FixedPagePrintTicket = printTicket;
 
 
 
            }
            else if(typeof(System.Windows.Media.Visual).IsAssignableFrom(_serializedObject.GetType()))
            {
                _documentSequenceXmlWriter = SimulateBeginFixedDocumentSequence();
                _documentXmlWriter         = SimulateBeginFixedDocument();
                if(!mode)
                {
                    _pageXmlWriter             = SimulateBeginFixedPage();
                }
            }
        }
 
 
        internal
        void
        EndConfirmToXPSStructure(
            bool mode
            )
        {
            if((_serializedObject.GetType() == typeof(System.Windows.Documents.FixedDocument)) ||
               (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(_serializedObject.GetType())
                && (_serializedObject.GetType() != typeof(System.Windows.Documents.FixedDocumentSequence))))
            {
                SimulateEndFixedDocumentSequence(_documentSequenceXmlWriter);
            }
            else if(_serializedObject.GetType() == typeof(System.Windows.Documents.FixedPage))
            {
                SimulateEndFixedDocument(_documentXmlWriter);
                SimulateEndFixedDocumentSequence(_documentSequenceXmlWriter);
            }
            else if(typeof(System.Windows.Media.Visual).IsAssignableFrom(_serializedObject.GetType()))
            {
                if(!mode)
                {
                    SimulateEndFixedPage(_pageXmlWriter);
                }
                SimulateEndFixedDocument(_documentXmlWriter);
                SimulateEndFixedDocumentSequence(_documentSequenceXmlWriter);
            }
        }
 
        internal
        virtual
        XmlWriter
        SimulateBeginFixedDocumentSequence(
            )
        {
            XmlWriter xmlWriter = null;
 
            if( _serializationManager is XpsSerializationManager)
            {
                (_serializationManager as XpsSerializationManager).RegisterDocumentSequenceStart();
            }
 
            //
            // Get the correct namespace
            //
            String xmlnsForType = _serializationManager.GetXmlNSForType(typeof(FixedDocumentSequence));
            //
            // Pick the correct XmlWriter
            //
            xmlWriter = _serializationManager.AcquireXmlWriter(typeof(FixedDocumentSequence));
            //
            // Write the start element and the namespace
            //
            if(xmlnsForType == null)
            {
                xmlWriter.WriteStartElement(XpsS0Markup.FixedDocumentSequence);
            }
            else
            {
                xmlWriter.WriteStartElement(XpsS0Markup.FixedDocumentSequence,
                                            xmlnsForType);
            }
            //
            // Pick the data for the PrintTicket if it existed
            //
            XpsSerializationPrintTicketRequiredEventArgs e =
            new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedDocumentSequencePrintTicket,
                                                             0);
 
            SimulatePrintTicketRaisingEvent(e);
 
            return xmlWriter;
        }
 
        internal
        virtual
        void
        SimulateEndFixedDocumentSequence(
            XmlWriter xmlWriter
            )
        {
            //
            // Write the End Element
            //
            xmlWriter.WriteEndElement();
            //
            // Release the Package writer
            //
            _serializationManager.ReleaseXmlWriter(typeof(FixedDocumentSequence));
            //
            // Inform any registered listener that the document sequence has been serialized
            //
            XpsSerializationProgressChangedEventArgs progressEvent =
            new XpsSerializationProgressChangedEventArgs(XpsWritingProgressChangeLevel.FixedDocumentSequenceWritingProgress,
                                                         0,
                                                         0,
                                                         null);
            if( _serializationManager is XpsSerializationManager)
            {
               (_serializationManager as XpsSerializationManager).RegisterDocumentSequenceEnd();
            }
            (_serializationManager as IXpsSerializationManager)?.OnXPSSerializationProgressChanged(progressEvent);
        }
 
 
        internal
        virtual
        XmlWriter
        SimulateBeginFixedDocument(
            )
        {
            XmlWriter   xmlWriter = null;
            if( _serializationManager is XpsSerializationManager)
            {
               (_serializationManager as XpsSerializationManager).RegisterDocumentStart();
            }
            //
            // Build the Image Table
            //
            _serializationManager.ResourcePolicy.ImageCrcTable = new Dictionary<UInt32, Uri>();
 
            _serializationManager.ResourcePolicy.ImageUriHashTable = new Dictionary<int, Uri>();
 
            //
            // Build the ColorContext Table
            //
            _serializationManager.ResourcePolicy.ColorContextTable = new Dictionary<int, Uri>();
            //
            // Get the correct namespace
            //
            String xmlnsForType = _serializationManager.GetXmlNSForType(typeof(FixedDocument));
            //
            // Pick the correct XmlWriter
            //
            xmlWriter = _serializationManager.AcquireXmlWriter(typeof(FixedDocument));
            //
            // Write the start element and the namespace
            //
            if(xmlnsForType == null)
            {
                xmlWriter.WriteStartElement("FixedDocument");
            }
            else
            {
                xmlWriter.WriteStartElement("FixedDocument",xmlnsForType);
            }
 
            XpsSerializationPrintTicketRequiredEventArgs e =
            new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedDocumentPrintTicket,
                                                             0);
 
            SimulatePrintTicketRaisingEvent(e);
 
            return xmlWriter;
        }
 
        internal
        virtual
        void
        SimulateEndFixedDocument(
            XmlWriter xmlWriter
            )
        {
            //
            // Write the End Element
            //
            xmlWriter.WriteEndElement();
            //
            // Release the Package writer
            //
            _serializationManager.ReleaseXmlWriter(typeof(FixedDocument));
            //
            // Clear off the table from the packaging policy
            //
            _serializationManager.ResourcePolicy.ImageCrcTable = null;
 
            _serializationManager.ResourcePolicy.ImageUriHashTable = null;
            //
            // Clear off the table from the packaging policy
            //
            _serializationManager.ResourcePolicy.ColorContextTable = null;
            //
            // Inform any registered listener that the document has been serialized
            //
            XpsSerializationProgressChangedEventArgs progressEvent =
            new XpsSerializationProgressChangedEventArgs(XpsWritingProgressChangeLevel.FixedDocumentWritingProgress,
                                                         0,
                                                         0,
                                                         null);
            if( _serializationManager is XpsSerializationManager)
            {
               (_serializationManager as XpsSerializationManager).RegisterDocumentEnd();
            }
            (_serializationManager as IXpsSerializationManager)?.OnXPSSerializationProgressChanged(progressEvent);
        }
 
        internal
        XmlWriter
        SimulateBeginFixedPage(
            )
        {
            XmlWriter   xmlWriter = null;
            (_serializationManager as IXpsSerializationManager)?.RegisterPageStart();
            //
            // Build the current Page Image Table
            //
            _serializationManager.ResourcePolicy.CurrentPageImageTable = new Dictionary<int, Uri>();
            //
            // Build the current Page ColorContext Table
            //
            _serializationManager.ResourcePolicy.CurrentPageColorContextTable = new Dictionary<int, Uri>();
            //
            // Get the correct namespace
            //
            String xmlnsForType = _serializationManager.GetXmlNSForType(typeof(FixedPage));
            //
            // Pick the correct XmlWriter
            //
            xmlWriter = _serializationManager.AcquireXmlWriter(typeof(FixedPage));
 
            Visual visual = _serializedObject as Visual;
            if (visual != null)
            {
                _treeWalker = new ReachTreeWalker(xmlWriter,_serializationManager);
                _treeWalker.SerializeLinksInVisual(visual);
            }
 
            //
            // Write the start element and the namespace
            //
            if(xmlnsForType == null)
            {
                xmlWriter.WriteStartElement(XpsS0Markup.FixedPage);
            }
            else
            {
                xmlWriter.WriteStartElement(XpsS0Markup.FixedPage);
 
                xmlWriter.WriteAttributeString(XpsS0Markup.Xmlns, xmlnsForType);
                xmlWriter.WriteAttributeString(XpsS0Markup.XmlnsX, XpsS0Markup.XmlnsXSchema);
                xmlWriter.WriteAttributeString(XpsS0Markup.XmlLang, XpsS0Markup.XmlLangValue);
            }
 
            //
            // Simulating the PrintTicket
            //
            XpsSerializationPrintTicketRequiredEventArgs e =
            new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedPagePrintTicket,
                                                             0);
 
            SimulatePrintTicketRaisingEvent(e);
 
            //
            // Adding Width and Height being mandatory attributes on the FixedPage
            //
            Size size = SimulateFixedPageSize(visual, e.PrintTicket);
 
            ((IXpsSerializationManager)_serializationManager).FixedPageSize = size;
 
            xmlWriter.WriteAttributeString(XpsS0Markup.PageWidth,
                                           TypeDescriptor.GetConverter(size.Width).ConvertToInvariantString(size.Width));
            xmlWriter.WriteAttributeString(XpsS0Markup.PageHeight,
                                           TypeDescriptor.GetConverter(size.Height).ConvertToInvariantString(size.Height));
 
            return xmlWriter;
        }
 
        internal
        void
        SimulateEndFixedPage(
            XmlWriter  xmlWriter
            )
        {
 
            _serializationManager.PackagingPolicy.PreCommitCurrentPage();
 
            if (_treeWalker != null)
            {
                _treeWalker.CommitHyperlinks();
                _treeWalker = null;
            }
 
            xmlWriter.WriteEndElement();
 
            _serializationManager.ReleaseXmlWriter(typeof(FixedPage));
            //
            // Clear off the table from the packaging policy
            //
            _serializationManager.ResourcePolicy.CurrentPageImageTable = null;
            //
            // Clear off the table from the packaging policy
            //
            _serializationManager.ResourcePolicy.CurrentPageColorContextTable = null;
            (_serializationManager as IXpsSerializationManager)?.VisualSerializationService.ReleaseVisualTreeFlattener();
            //
            // Inform any registered listener that the page has been serialized
            //
            XpsSerializationProgressChangedEventArgs progressEvent =
            new XpsSerializationProgressChangedEventArgs(XpsWritingProgressChangeLevel.FixedPageWritingProgress,
                                                         0,
                                                         0,
                                                         null);
            (_serializationManager as IXpsSerializationManager)?.OnXPSSerializationProgressChanged(progressEvent);
            (_serializationManager as IXpsSerializationManager)?.RegisterPageEnd();
        }
 
        #endregion Internal Methods
 
        #region Private Methods
        
        protected
        void
        SimulatePrintTicketRaisingEvent(
            XpsSerializationPrintTicketRequiredEventArgs e
            )
        {
            (_serializationManager as IXpsSerializationManager)?.OnXPSSerializationPrintTicketRequired(e);
 
            //
            // Serialize the data for the PrintTicket
            //
            if(e.Modified)
            {
                if(e.PrintTicket != null)
                {
                    PrintTicketSerializer serializer = new PrintTicketSerializer(_serializationManager);
                    serializer.SerializeObject(e.PrintTicket);
                }
            }
 
        }
 
              
        Size
        SimulateFixedPageSize(
            Visual visual,
            PrintTicket printTicket
            )
        {
            Size sz = new Size(0,0);
 
            //Try to cast the visual down to a UIElement so we can get the PreviousArrangeSize
            //
            UIElement element = visual as UIElement;
            if (element != null)
            {
                Rect rect = element.PreviousArrangeRect;
                sz = Toolbox.Layout(element, rect.Size, printTicket);
            }
            else
            {
                //If the visual is not a UIElement, call ValidateDocumentSize to assign defaults
                sz = Toolbox.ValidateDocumentSize(sz, printTicket);
            }
 
            return sz;
        }
 
        #endregion Private Methods
 
        #region Protected Data
 
        protected
        PackageSerializationManager   _serializationManager;
 
        #endregion Protected Data
 
        #region Private Data
 
        private
        Object                        _serializedObject;
 
        private
        XmlWriter                     _documentSequenceXmlWriter;
 
        private
        XmlWriter                     _documentXmlWriter;
 
        private
        XmlWriter                     _pageXmlWriter;
 
        private
        ReachTreeWalker             _treeWalker;
 
        #endregion Private Data
    };
 
    internal class ReachTreeWalker
    {
        #region Constructors
 
        internal
        ReachTreeWalker(
            ReachSerializer serializer
            )
        {
            _serializerXmlWriter = serializer.XmlWriter;
            _serializationManager = serializer.SerializationManager;
        }
 
        internal
        ReachTreeWalker(
            XmlWriter writer,
            PackageSerializationManager serializationManager)
        {
            _serializerXmlWriter = writer;
            _serializationManager = serializationManager;
        }
        #endregion
 
        #region Internal Methods
        internal void CommitHyperlinks()
        {
            //copy hyperlinks into stream
            if (_linkStream != null)
            {
                _linkStream.Close();
                XmlWriter.WriteRaw(_linkStream.ToString());
            }
        }
 
        internal void SerializeLinksInDocumentPage(DocumentPage page)
        {
            IContentHost contentHost = page as IContentHost;
            Visual root = page.Visual;
 
            if (contentHost != null)
            {
                SerializeLinksForIContentHost(contentHost, root);
            }
            else
            {
                // do logical tree
                SerializeLinksInLogicalTree(root, null, root);
            }
 
        }
 
        internal void SerializeLinksInFixedPage(FixedPage page)
        {
            System.Collections.IEnumerable enumerable = page.Children;
            foreach (object element in enumerable)
            {
                // Checks and sees if this element has an ID or is a Hyperlink
                if (element is IInputElement)
                {
                    SerializeLinkTargetForElement((IInputElement)element, null, page);
                }
 
                if (element is IContentHost)
                {
                    //recursively find links in its elements
                    SerializeLinksForIContentHost((IContentHost)element, page);
                }
                else if (element is FrameworkElement)
                {
                    // do logical tree
                    SerializeLinksInLogicalTree((DependencyObject)element, null, page);
                }
            }
 
        }
 
        internal void SerializeLinksInVisual(Visual visual)
        {
            // Checks and sees if this element has an ID or is a Hyperlink
            if (visual is IInputElement)
            {
                SerializeLinkTargetForElement((IInputElement)visual, null, visual);
            }
 
            IContentHost contentHost = visual as IContentHost;
            if (contentHost != null)
            {
                //recursively find links in its elements
                SerializeLinksForIContentHost(contentHost, visual);
            }
            else
            {
                // do logical tree
                SerializeLinksInLogicalTree(visual, null, visual);
            }
        }
 
        #endregion Internal Methods
 
        #region Private Methods
        private void SerializeLinksForIContentHost(IContentHost contentHost, Visual root)
        {
            System.Collections.Generic.IEnumerator<IInputElement> enumerator = contentHost.HostedElements;
            while (enumerator.MoveNext())
            {
                IInputElement element = enumerator.Current;
 
                // Checks and sees if this element has an ID or is a Hyperlink
                SerializeLinkTargetForElement(element, contentHost, root);
 
                if (element is IContentHost)
                {
                    //recursively find links in its elements
                    SerializeLinksForIContentHost((IContentHost)element, root);
                }
                else if (element is FrameworkElement)
                {
                    // do we do this for FrameworkContentElement?  We could...  But do we want to?
                    // do logical tree
                    SerializeLinksInLogicalTree((DependencyObject)element, contentHost, root);
                }
            }
        }
 
        private void SerializeLinksInLogicalTree(DependencyObject dependencyObject, IContentHost contentHost, Visual root)
        {
            System.Collections.IEnumerable enumerable = LogicalTreeHelper.GetChildren(dependencyObject);
            foreach (object element in enumerable)
            {
                // Checks and sees if this element has an ID or is a Hyperlink
                if (element is IInputElement)
                {
                    SerializeLinkTargetForElement((IInputElement)element, contentHost, root);
                }
 
                if (element is IContentHost)
                {
                    //recursively find links in its elements
                    SerializeLinksForIContentHost((IContentHost)element, root);
                }
                else if (element is FrameworkElement)
                {
                    // do we do this for FrameworkContentElement?  We could...  But do we want to?
                    // do logical tree
                    SerializeLinksInLogicalTree((DependencyObject)element, contentHost, root);
                }
            }
        }
 
        private void SerializeLinkTargetForElement(IInputElement element, IContentHost contentHost, Visual root)
        {
            if (element is FrameworkElement)
            {
                FrameworkElement fe = (FrameworkElement)element;
                string id = fe.Name;
                if (!String.IsNullOrEmpty(id))
                {
                    // No need to add overlapping path element for named FE element.
                    AddLinkTarget(id);
                }
            }
            else if (element is FrameworkContentElement && contentHost != null)
            {
                FrameworkContentElement fce = (FrameworkContentElement)element;
                string id = fce.Name;
                Uri uri = null;
 
                if (element is Hyperlink)
                {
                    uri = ((Hyperlink)element).NavigateUri;
                }
 
                if (!String.IsNullOrEmpty(id) || uri != null)
                {
                    Transform transform = Transform.Identity;
                    Visual contentVisual = contentHost as Visual;
                    if (contentVisual != null && root != null && root.IsAncestorOf(contentVisual))
                    {
                        GeneralTransform t = contentVisual.TransformToAncestor(root);
 
                        if (t is Transform)
                        {
                            transform = (Transform)t;
                        }
                    }
 
                    Transform rootTransform = root.VisualTransform;
                    if (rootTransform != null)
                    {
                        transform = new MatrixTransform(Matrix.Multiply(transform.Value, rootTransform.Value));
                    }
                    transform = new MatrixTransform(
                        Matrix.Multiply(transform.Value,
                            new TranslateTransform(root.VisualOffset.X, root.VisualOffset.Y).Value
                            )
                        );
 
 
                    PathGeometry geometry = new PathGeometry();
                    System.Collections.ObjectModel.ReadOnlyCollection<Rect> rectangles = contentHost.GetRectangles(fce);
                    foreach (Rect rect in rectangles)
                    {
                        geometry.AddGeometry(new RectangleGeometry(rect, 0, 0, transform));
                    }
                    SerializeHyperlink(geometry, id, uri);
                }
            }
        }
 
        private bool IsFragment(Uri uri)
        {
            return uri.OriginalString.StartsWith(FRAGMENTMARKER,StringComparison.Ordinal);
        }
 
        private void SerializeHyperlink(PathGeometry geometry, String id, Uri navigateUri)
        {
            String nameForType = "Path";
            XmlWriter writer = LinkXmlWriter;
 
            bool useID = false;
            if (!String.IsNullOrEmpty(id))
            {
                useID = AddLinkTarget(id);
            }
 
            if (!useID && navigateUri == null)
            {
                // don't write out this path
                return;
            }
 
            writer.WriteStartElement(nameForType);
 
            if (navigateUri != null)
            {
                if (IsFragment(navigateUri))
                {
                    XpsPackagingPolicy policy = SerializationManager.PackagingPolicy as XpsPackagingPolicy;
                    XpsOMPackagingPolicy omPolicy = SerializationManager.PackagingPolicy as XpsOMPackagingPolicy;
                    if (policy == null && omPolicy == null)
                    {
                        throw new XpsSerializationException(SR.ReachSerialization_WrongPackagingPolicy);
                    }
 
                    Uri documentUri = SerializationManager.PackagingPolicy.CurrentFixedDocumentUri;
                    Uri pageUri = SerializationManager.PackagingPolicy.CurrentFixedPageUri;
 
                    Uri relativeUri = PackUriHelper.GetRelativeUri(pageUri, documentUri);
                    string documentFragmentUri = relativeUri.OriginalString + navigateUri.OriginalString;
                    WriteAttribute(writer, "FixedPage.NavigateUri", documentFragmentUri);
                }
                else
                {
                    WriteAttribute(writer, "FixedPage.NavigateUri", navigateUri);
                }
                WriteAttribute(writer, "Fill", "#00000000");
            }
            else
            {
                WriteAttribute(writer, "Opacity", "0");
            }
 
            if (useID)
            {
                WriteAttribute(writer, "Name", id);
            }
 
            geometry.FillRule = FillRule.Nonzero;
 
            Size pageSize = ((IXpsSerializationManager)_serializationManager).FixedPageSize;
            VisualTreeFlattener.WritePath(writer, geometry, pageSize);
 
            writer.WriteEndElement();
        }
 
        private void WriteAttribute(XmlWriter writer, string name, object value)
        {
            writer.WriteAttributeString(name, TypeDescriptor.GetConverter(value).ConvertToInvariantString(value));
        }
 
        private bool AddLinkTarget(string name)
        {
            if (LinkTargetList != null && !LinkTargetList.Contains(name))
            {
                LinkTargetList.Add(name);
                return true;
            }
            return false;
        }
        #endregion Private Methods
 
        #region Private Properties
        private PackageSerializationManager SerializationManager
        {
            get
            {
                return _serializationManager;
            }
        }
        /// <summary>
        ///
        /// </summary>
        private
        XmlWriter
        XmlWriter
        {
            get
            {
                return _serializerXmlWriter;
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        private
        XmlWriter
        LinkXmlWriter
        {
            get
            {
                if (_linkXmlWriter == null)
                {
                    _linkStream = new StringWriter(CultureInfo.InvariantCulture);
                    _linkXmlWriter = new XmlTextWriter(_linkStream);
                }
 
                return _linkXmlWriter;
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        private
        IList<String>
        LinkTargetList
        {
            get
            {
                if (_linkTargetList == null)
                {
                    IXpsSerializationManager rsm = SerializationManager as IXpsSerializationManager;
                    if (rsm != null)
                    {
                        XmlWriter writer = this.XmlWriter; // guarantee page is created
                        _linkTargetList = SerializationManager.PackagingPolicy.AcquireStreamForLinkTargets();
                    }
                }
 
                return _linkTargetList;
            }
        }
        #endregion Private Properties
 
        #region Private Fields
        private IList<String>  _linkTargetList;
        private XmlWriter _linkXmlWriter;
        private StringWriter _linkStream;
        private XmlWriter _serializerXmlWriter;
        private PackageSerializationManager _serializationManager;
        private const string FRAGMENTMARKER = "#";
        #endregion
    };
 
 
    internal static class Toolbox
    {
        internal static void EmitEvent(EventTrace.Event evt)
        {
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordXPS, EventTrace.Level.Info, evt);
        }
 
        //
        // Layout -
        // Gets the size of the FixedPage and delegates the task of Laying out the UIElement to the overloaded
        // Layout method.  If the size changes, we assign the new size to FixedPage so the properties are serialized
        // correctly.
        //
        internal
        static
        void
        Layout(
            FixedPage fixedPage,
            PrintTicket printTicket
        )
        {
            Size size = new Size(fixedPage.Width, fixedPage.Height);
            Size newSize = Layout(fixedPage, size, printTicket);
 
            if (size != newSize)
            {
                fixedPage.Width = newSize.Width;
                fixedPage.Height = newSize.Height;
            }
        }
 
 
        //
        // Layout - Calls ValidateDocumentSize to validate the document size or to assign defaults.
        // Lays out the visual if the size changes or if the visual has not been layed out yet.
        //
        internal
        static
        Size
        Layout(
            UIElement uiElement,
            Size elementSize,
            PrintTicket printTicket
            )
        {
            Size newSize = ValidateDocumentSize(elementSize, printTicket);
 
            if (uiElement.IsArrangeValid == false ||
                uiElement.IsMeasureValid == false ||
                elementSize != newSize
                )
            {
                EmitEvent(EventTrace.Event.WClientDRXLayoutBegin);
 
                uiElement.Measure(newSize);
                uiElement.Arrange(new Rect(new Point(), newSize));
                uiElement.UpdateLayout();
 
                EmitEvent(EventTrace.Event.WClientDRXLayoutEnd);
            }
 
            return newSize;
        }
 
 
        //
        // ValidateDocumentSize -
        // Checks to see if the document size is valid.  If not, first try to see if the desired
        // size can be determined by the printTicket.  If that fails, assign default fixedpage size
        // as last resort.
        //
        internal
        static
        Size
        ValidateDocumentSize(Size documentSize, PrintTicket printTicket)
        {
            if (documentSize.Width == 0 ||
               documentSize.Height == 0 ||
               double.IsNaN(documentSize.Width) ||
               double.IsNaN(documentSize.Height) ||
               Double.IsPositiveInfinity(documentSize.Width) ||
               Double.IsPositiveInfinity(documentSize.Height) ||
               Double.IsNegativeInfinity(documentSize.Width) ||
               Double.IsNegativeInfinity(documentSize.Height)
               )
            {
                Size sz = new Size(0, 0);
 
                //if print ticket definied, use printTicket dimensions
                if (printTicket != null &&
                    printTicket.PageMediaSize != null &&
                    printTicket.PageMediaSize.Width.HasValue &&
                    printTicket.PageMediaSize.Height.HasValue)
                {
                    // Swap the height and width if needed to obtain a landscape orientation.
                    if (printTicket.PageOrientation == PageOrientation.Landscape
                        || printTicket.PageOrientation == PageOrientation.ReverseLandscape)
                    {
                        sz.Width = printTicket.PageMediaSize.Height.Value;
                        sz.Height = printTicket.PageMediaSize.Width.Value;
                    }
                    else
                    {
                        sz.Width = printTicket.PageMediaSize.Width.Value;
                        sz.Height = printTicket.PageMediaSize.Height.Value;
                    }
                }
                else
                {
                    sz.Width = 96 * 8.5;
                    sz.Height = 96 * 11;
                }
                return sz;
            }
 
            return documentSize;
        }
 
 
        internal static DocumentPage GetPage(DocumentPaginator paginator, int index)
        {
            EmitEvent(EventTrace.Event.WClientDRXGetPageBegin);
 
            DocumentPage page = paginator.GetPage(index);
 
            EmitEvent(EventTrace.Event.WClientDRXGetPageEnd);
 
            return page;
        }
 
        internal static FixedPage GetPageRoot(object page)
        {
            FixedPage fixedPage = ((PageContent) page).GetPageRoot(false) as FixedPage;
 
            return fixedPage;
        }
    }
}