File: Serialization\Manager\NGCSerializationManager.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.Xml;
using System.Windows.Xps.Packaging;
using System.Windows.Media;
using System.Printing;
using MS.Utility;
 
using Microsoft.Internal.AlphaFlattener;
//
// Ngc = Next Generation Converter. It means to convert the avalon element tree
//  to the downlevel GDI primitives.
//
namespace System.Windows.Xps.Serialization
{
    /// <summary>
    /// NgcSerializationManager is a class to help print avalon content (element tree) to traditional printer
    /// </summary>
    //[CLSCompliant(false)]
    internal sealed class NgcSerializationManager :
                          PackageSerializationManager
    {
        #region Constructor
        /// <summary>
        /// This constructor take PrintQueue parameter
        /// </summary>
        /// <exception cref="ArgumentNullException">queue is NULL.</exception>
        public
        NgcSerializationManager(
            PrintQueue   queue,
            bool         isBatchMode
            ):
        base()
        {
            ArgumentNullException.ThrowIfNull(queue);
            _printQueue                 = queue;
            this._isBatchMode           = isBatchMode;
            this._isSimulating          = false;
            this._printTicketManager    = new NgcPrintTicketManager(_printQueue);
        }
        #endregion Construtor
 
        #region PackageSerializationManager override
        /// <summary>
        /// The function will serializer the avalon content to the printer spool file.
        /// SaveAsXaml is not a propriate name. Maybe it should be "Print"
        /// </summary>
        /// <exception cref="ArgumentNullException">serializedObject is NULL.</exception>
        /// <exception cref="XpsSerializationException">serializedObject is not a supported type.</exception>
        public
        override
        void
        SaveAsXaml(
            Object  serializedObject
            )
        {
            Toolbox.EmitEvent(EventTrace.Event.WClientDRXSaveXpsBegin);
 
            ArgumentNullException.ThrowIfNull(serializedObject);
 
            if (!IsSerializedObjectTypeSupported(serializedObject))
            {
                throw new XpsSerializationException(SR.ReachSerialization_NotSupported);
            }
 
            if(_isBatchMode && !_isSimulating)
            {
                XpsSerializationPrintTicketRequiredEventArgs printTicketEvent =
                new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedDocumentSequencePrintTicket,
                                                                 0);
                OnNGCSerializationPrintTicketRequired(printTicketEvent);
 
                StartDocument(null,true);
                _isSimulating = true;
            }
 
            if(_isBatchMode)
            {
                StartPage();
            }
 
            if(!_isBatchMode &&
               IsDocumentSequencePrintTicketRequired(serializedObject))
            {
                XpsSerializationPrintTicketRequiredEventArgs printTicketEvent =
                new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedDocumentSequencePrintTicket,
                                                                 0);
                OnNGCSerializationPrintTicketRequired(printTicketEvent);
            }
 
 
            ReachSerializer reachSerializer = GetSerializer(serializedObject);
 
 
            if(reachSerializer != null)
            {
                //
                // Call top-level type serializer, it will walk through the contents and
                // all CLR and DP properties of the object and invoke the proper serializer
                // and typeconverter respectively
                //
 
                reachSerializer.SerializeObject(serializedObject);
 
                if(_isBatchMode)
                {
                    EndPage();
                }
            }
            else
            {
                throw new XpsSerializationException(SR.ReachSerialization_NoSerializer);
            }
 
            Toolbox.EmitEvent(EventTrace.Event.WClientDRXSaveXpsEnd);
        }
 
 
        /// <summary>
        ///
        /// </summary>
        public
        void
        Cancel(
            )
        {
            if(_startDocCnt != 0 )
            {
                _device.AbortDocument();
                _startDocCnt = 0;
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        public
        void
        Commit(
            )
        {
            if(_isBatchMode && _isSimulating)
            {
                EndDocument();
            }
            else
            {
                //
                // throw the appropariate exception
                //
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        public
        event
        XpsSerializationPrintTicketRequiredEventHandler     XpsSerializationPrintTicketRequired;
 
        /// <summary>
        ///
        /// </summary>
        public
        event
        XpsSerializationProgressChangedEventHandler         XpsSerializationProgressChanged;
 
 
        /// <summary>
        /// This function GetXmlNSForType and the other four XML writer functiosn (like AcquireXmlWriter)
        /// and the two stream function (like ReleaseResourceStream) should be removed from
        /// the base PackageSerializationManager class.
        /// </summary>
        internal
        override
        String
        GetXmlNSForType(
            Type    objectType
            )
        {
            return null;
        }
 
        /// <summary>
        ///
        /// </summary>
        internal
        override
        ReachSerializer
        GetSerializer(
            Object serializedObject
            )
        {
            ReachSerializer reachSerializer = null;
 
            if((reachSerializer = base.GetSerializer(serializedObject)) == null)
            {
                reachSerializer = this.SerializersCacheManager.GetSerializer(serializedObject);
            }
 
            return reachSerializer;
        }
 
        /// <summary>
        ///
        /// </summary>
        internal
        override
        Type
        GetSerializerType(
            Type objectType
            )
        {
            Type serializerType = null;
 
 
            if((serializerType = base.GetSerializerType(objectType)) == null)
            {
                if (typeof(System.Windows.Documents.FixedDocumentSequence).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcDocumentSequenceSerializer);
                }
                else if (typeof(System.Windows.Documents.DocumentReferenceCollection).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcDocumentReferenceCollectionSerializer);
                }
                else if (typeof(System.Windows.Documents.FixedDocument).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcFixedDocumentSerializer);
                }
                else if(typeof(System.Windows.Documents.PageContentCollection).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NGCReachPageContentCollectionSerializer);
                }
                else if(typeof(System.Windows.Documents.PageContent).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NGCReachPageContentSerializer);
                }
                else if(typeof(System.Windows.Controls.UIElementCollection).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NGCReachUIElementCollectionSerializer);
                }
                else if(typeof(System.Windows.Documents.FixedPage).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcFixedPageSerializer);
                }
                else if (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcDocumentPaginatorSerializer);
                }
                else if (typeof(System.Windows.Documents.DocumentPage).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcDocumentPageSerializer);
                }
                else if (typeof(System.Windows.Media.Visual).IsAssignableFrom(objectType))
                {
                    serializerType = typeof(NgcReachVisualSerializer);
                }
 
 
            }
 
            return serializerType;
        }
 
        internal
        override
        XmlWriter
        AcquireXmlWriter(
            Type    writerType
            )
        {
            return null;
        }
 
        internal
        override
        void
        ReleaseXmlWriter(
            Type    writerType
            )
        {
            return;
        }
 
        internal
        override
        XpsResourceStream
        AcquireResourceStream(
            Type    resourceType
            )
        {
            return null;
        }
 
        internal
        override
        XpsResourceStream
        AcquireResourceStream(
            Type    resourceType,
            String  resourceID
            )
        {
            return null;
        }
 
        internal
        override
        void
        ReleaseResourceStream(
            Type    resourceType
            )
        {
            return;
        }
 
        internal
        override
        void
        ReleaseResourceStream(
            Type    resourceType,
            String  resourceID
            )
        {
            return;
        }
 
        internal
        override
        void
        AddRelationshipToCurrentPage(
            Uri targetUri,
            string relationshipName
            )
        {
            return;
        }
 
 
        internal
        override
        XpsResourcePolicy
        ResourcePolicy
        {
            get
            {
                return null;
            }
        }
 
        internal
        override
        BasePackagingPolicy
        PackagingPolicy
        {
            get
            {
                return null;
            }
        }
 
        #endregion PackageSerializationManager override
 
        #region Internal Properties
        /// <summary>
        ///
        /// </summary>
        internal
        PrintQueue
        PrintQueue
        {
            get
            {
                return _printQueue;
            }
        }
 
        internal
        String
        JobName
        {
            set
            {
                if (_jobName == null)
                {
                    _jobName = value;
                }
            }
 
            get
            {
                return _jobName;
            }
        }
 
        internal
        Size
        PageSize
        {
            set
            {
                _pageSize = value;
            }
            get
            {
                return _pageSize;
            }
        }
 
        #endregion Internal Properties
 
        #region Internal Methods
        internal
        void
        StartDocument(
            Object o,
            bool   documentPrintTicketRequired
            )
        {
            if(documentPrintTicketRequired)
            {
                XpsSerializationPrintTicketRequiredEventArgs e =
                    new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedDocumentPrintTicket,
                                                                     0);
                OnNGCSerializationPrintTicketRequired(e);
            }
 
            if( _startDocCnt == 0 )
            {
                JobName = _printQueue.CurrentJobSettings.Description;
 
                if(JobName == null)
                {
                    JobName = NgcSerializerUtil.InferJobName(o);
                }
 
                _device  = new MetroToGdiConverter(PrintQueue);
 
                if (!_isSimulating)
                {
                    JobIdentifier = _device.StartDocument(_jobName, _printTicketManager.ConsumeActivePrintTicket(true));
                }
            }
 
            _startDocCnt++;
        }
 
        /// <summary>
        ///
        /// </summary>
        ///
        internal
        void
        EndDocument()
        {
            if( _startDocCnt == 1 )
            {
                _device.EndDocument();
 
                //
                // Inform any potential listeners that the document has been printed
                //
                XpsSerializationProgressChangedEventArgs e =
                new XpsSerializationProgressChangedEventArgs(XpsWritingProgressChangeLevel.FixedDocumentWritingProgress,
                                                             0,
                                                             0,
                                                             null);
                OnNGCSerializationProgressChagned(e);
            }
            _startDocCnt--;
        }
 
        /// <summary>
        ///
        /// </summary>
        ///
        internal
        bool
        StartPage()
        {
            bool    bManulStartDoc = false;
 
            if (_startDocCnt == 0)
            {
                StartDocument(null,true);
                bManulStartDoc = true;
            }
 
            if(!_isStartPage)
            {
                if (_isPrintTicketMerged == false)
                {
                    XpsSerializationPrintTicketRequiredEventArgs e =
                    new XpsSerializationPrintTicketRequiredEventArgs(PrintTicketLevel.FixedPagePrintTicket,
                                                                     0);
                    OnNGCSerializationPrintTicketRequired(e);
                }
 
                _device.StartPage(_printTicketManager.ConsumeActivePrintTicket(true));
                _isStartPage = true;
            }
 
            return bManulStartDoc;
        }
 
        /// <summary>
        ///
        /// </summary>
        ///
        internal
        void
        EndPage()
        {
            if (_isStartPage)
            {
                try
                {
                    _device.FlushPage();
                }
                catch (PrintingCanceledException )
                {
                    _device.EndDocument();
                    throw;
                }
            }
 
            _isStartPage = false;
            _isPrintTicketMerged = false;
 
            //
            // Inform any potential listeners that the page has been printed
            //
            XpsSerializationProgressChangedEventArgs e =
            new XpsSerializationProgressChangedEventArgs(XpsWritingProgressChangeLevel.FixedPageWritingProgress,
                                                         0,
                                                         0,
                                                         null);
            OnNGCSerializationProgressChagned(e);
        }
 
        internal
        void
        OnNGCSerializationPrintTicketRequired(
            object operationState
            )
        {
            XpsSerializationPrintTicketRequiredEventArgs e = operationState as XpsSerializationPrintTicketRequiredEventArgs;
 
            if(XpsSerializationPrintTicketRequired != null)
            {
                e.Modified = true;
 
                if (e.PrintTicketLevel == PrintTicketLevel.FixedPagePrintTicket)
                {
                    _isPrintTicketMerged = true;
                }
 
                XpsSerializationPrintTicketRequired(this,e);
 
                _printTicketManager.ConstructPrintTicketTree(e);
            }
        }
 
        internal
        void
        OnNGCSerializationProgressChagned(
            object operationState
            )
        {
            XpsSerializationProgressChangedEventArgs e = operationState as XpsSerializationProgressChangedEventArgs;
 
            if(XpsSerializationProgressChanged != null)
            {
                XpsSerializationProgressChanged(this,e);
            }
        }
 
        /// <summary>
        ///
        /// </summary>
        ///
        internal
        void
        WalkVisual(
            Visual visual
            )
        {
            bool    bManulStartDoc = false;
            bool    bManulStartpage = false;
 
            if (_startDocCnt == 0)
            {
                StartDocument(visual,true);
                bManulStartDoc = true;
            }
            if (!_isStartPage)
            {
                StartPage();
                bManulStartpage = true;
            }
 
            //
            // Call VisualTreeFlattener to flatten the visual on IMetroDrawingContext
            //
            Toolbox.EmitEvent(EventTrace.Event.WClientDRXLoadPrimitiveBegin);
 
            VisualTreeFlattener.Walk(visual, _device, PageSize, new TreeWalkProgress());
 
            Toolbox.EmitEvent(EventTrace.Event.WClientDRXLoadPrimitiveEnd);
 
            if (bManulStartpage)
            {
                EndPage();
            }
            if (bManulStartDoc)
            {
                EndDocument();
            }
        }
 
        internal
        PrintTicket
        GetActivePrintTicket()
        {
            return _printTicketManager.ActivePrintTicket;
        }
 
        internal
        bool
        IsPrintTicketEventHandlerEnabled
        {
            get
            {
                if(XpsSerializationPrintTicketRequired!=null)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
 
        private
        bool
        IsSerializedObjectTypeSupported(
            Object  serializedObject
            )
        {
            bool isSupported = false;
 
            Type serializedObjectType = serializedObject.GetType();
 
            if(_isBatchMode)
            {
                if((typeof(System.Windows.Media.Visual).IsAssignableFrom(serializedObjectType)) &&
                    (serializedObjectType != typeof(System.Windows.Documents.FixedPage)))
                {
                    isSupported = true;
                }
            }
            else
            {
                if ( (serializedObjectType == typeof(System.Windows.Documents.FixedDocumentSequence)) ||
                     (serializedObjectType == typeof(System.Windows.Documents.FixedDocument))    ||
                     (serializedObjectType == typeof(System.Windows.Documents.FixedPage))        ||
                     (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(serializedObjectType)) ||
                     (typeof(System.Windows.Media.Visual).IsAssignableFrom(serializedObjectType)) )
                {
                    isSupported = true;
                }
            }
 
            return isSupported;
        }
 
        private
        bool
        IsDocumentSequencePrintTicketRequired(
            Object  serializedObject
            )
        {
            bool isRequired = false;
 
            Type serializedObjectType = serializedObject.GetType();
 
            if ((serializedObjectType == typeof(System.Windows.Documents.FixedDocument))    ||
                (serializedObjectType == typeof(System.Windows.Documents.FixedPage))        ||
                (typeof(System.Windows.Documents.DocumentPaginator).IsAssignableFrom(serializedObjectType)) ||
                (typeof(System.Windows.Media.Visual).IsAssignableFrom(serializedObjectType)) )
            {
                isRequired = true;
            }
 
            return isRequired;
        }
 
        #endregion Internal Methods
 
        #region Private Member
        private     PrintQueue             _printQueue;
        private     int                    _startDocCnt;
        private     bool                   _isStartPage;
        private     MetroToGdiConverter    _device;
        private     String                 _jobName;
        private     bool                   _isBatchMode;
        private     bool                   _isSimulating;
        private     NgcPrintTicketManager  _printTicketManager;
        private     bool                   _isPrintTicketMerged;
        private     Size                    _pageSize = new Size(816,1056);
        #endregion Private Member
    };
 
    internal sealed class NgcPrintTicketManager
    {
        #region constructor
 
        public
        NgcPrintTicketManager(
            PrintQueue printQueue
            )
        {
            this._printQueue        = printQueue;
            this._fdsPrintTicket    = null;
            this._fdPrintTicket     = null;
            this._fpPrintTicket     = null;
            this._rootPrintTicket   = null;
            this._activePrintTicket = null;
            this._isActiveUpdated   = false;
 
            this.m_validatedPrintTicketCache = new MS.Internal.Printing.MostFrequentlyUsedCache<string, PrintTicket>(s_PrintTicketCacheMaxCount);
        }
 
        #endregion constructor
 
        #region Public Methods
 
        public
        void
        ConstructPrintTicketTree(
            XpsSerializationPrintTicketRequiredEventArgs    args
            )
        {
            switch(args.PrintTicketLevel)
            {
                case PrintTicketLevel.FixedDocumentSequencePrintTicket:
                {
                    if(args.PrintTicket != null)
                    {
                        _fdsPrintTicket  = args.PrintTicket;
                        _rootPrintTicket = _fdsPrintTicket;
                    }
                    else
                    {
                        _rootPrintTicket = new PrintTicket(_printQueue.UserPrintTicket.GetXmlStream());
                    }
 
                    _activePrintTicket = _rootPrintTicket;
                    _isActiveUpdated   = true;
 
                    break;
                }
 
                case PrintTicketLevel.FixedDocumentPrintTicket:
                {
                    if(args.PrintTicket != null)
                    {
                        _fdPrintTicket = args.PrintTicket;
 
                        if(_fdsPrintTicket!=null)
                        {
                            //
                            // we have to merge the 2 print tickets
                            //
                            _rootPrintTicket = _printQueue.
                                               MergeAndValidatePrintTicket(_fdsPrintTicket, _fdPrintTicket).
                                               ValidatedPrintTicket;
                        }
                        else
                        {
                            _rootPrintTicket = _fdPrintTicket;
                        }
                    }
                    else
                    {
                        _fdPrintTicket   = null;
                        _rootPrintTicket = _fdsPrintTicket;
                    }
 
                    if(_rootPrintTicket == null)
                    {
                        _rootPrintTicket = new PrintTicket(_printQueue.UserPrintTicket.GetXmlStream());
                    }
 
                    _activePrintTicket = _rootPrintTicket;
                    _isActiveUpdated   = true;
 
                    break;
                }
 
                case PrintTicketLevel.FixedPagePrintTicket:
                {
                    if(args.PrintTicket != null)
                    {
                        _fpPrintTicket = args.PrintTicket;
 
                        // The NULL check might not be needed but just in case...
                        String printTicketPairXMLStr =
                            (_rootPrintTicket == null ? "" : _rootPrintTicket.ToXmlString()) +
                            (_fpPrintTicket == null ? "" : _fpPrintTicket.ToXmlString());
 
                        PrintTicket newActivePrintTicket;
 
                        if (!m_validatedPrintTicketCache.TryGetValue(printTicketPairXMLStr, out newActivePrintTicket))
                        {
                            //
                            // we have to merge the 2 print tickets
                            //
                            newActivePrintTicket = _printQueue.
                                                 MergeAndValidatePrintTicket(_rootPrintTicket, _fpPrintTicket).
                                                 ValidatedPrintTicket;
 
                            m_validatedPrintTicketCache.CacheValue(printTicketPairXMLStr, newActivePrintTicket);
                        }
 
                        // Ensure cache values are immutable.
                        _activePrintTicket = newActivePrintTicket.Clone ();
 
                        _isActiveUpdated   = true;
                    }
                    else
                    {
                        if(_fpPrintTicket != null)
                        {
                            _fpPrintTicket     = null;
                            _activePrintTicket = _rootPrintTicket;
                            _isActiveUpdated   = true;
                        }
                    }
                    break;
                }
 
                default:
                {
                    break;
                }
            }
 
        }
 
        public
        PrintTicket
        ConsumeActivePrintTicket(
            bool consumePrintTicket
            )
        {
            PrintTicket printTicket = null;
 
            if (_activePrintTicket == null &&
               _rootPrintTicket == null)
            {
                _activePrintTicket = new PrintTicket(_printQueue.UserPrintTicket.GetXmlStream());
                _rootPrintTicket = _activePrintTicket;
                printTicket = _activePrintTicket;
            }
            else if (_isActiveUpdated)
            {
                printTicket = _activePrintTicket;
                if (consumePrintTicket)
                {
                    _isActiveUpdated = false;
                }
            }
 
            return printTicket;
        }
 
        #endregion Public Methods
 
        #region Public Properties
 
        public
        PrintTicket
        ActivePrintTicket
        {
            get
            {
                //Call Consume Active Print Ticket with false flag, to get the current print ticket but do not consume it
                //so it can be retrieved again
                return ConsumeActivePrintTicket(false);
            }
        }
 
        #endregion Public Properties
 
 
        #region Private Members
 
        // duplicated code with metrodevice.cs is bad
 
        // PrintTicket XML Strings start at 4KB so we don't want the keys
        // from chewing all memory.  Limit to a small amount.
        private const int s_PrintTicketCacheMaxCount = 10;
 
        // Key = Pair of PrintTicket's whose XML (as strings) are concatenated
        // Value = PrintTicket
        //
        // 1. Dictionary keys and values must never be:
        //    - null
        //    - mutated
        // 2. Only cache per-page calculations because cache entries are preciously
        //    limited.
        private
        MS.Internal.Printing.MostFrequentlyUsedCache<string, PrintTicket> m_validatedPrintTicketCache;
 
        private
        PrintQueue      _printQueue;
 
        private
        PrintTicket     _fdsPrintTicket;
 
        private
        PrintTicket     _fdPrintTicket;
 
        private
        PrintTicket     _fpPrintTicket;
 
        private
        PrintTicket     _rootPrintTicket;
 
        private
        PrintTicket     _activePrintTicket;
 
        private
        bool            _isActiveUpdated;
 
 
        #endregion Private Members
    }
 
 
    internal sealed class MXDWSerializationManager
    {
 
        #region constructor
 
        public
        MXDWSerializationManager(
            PrintQueue   queue
            )
        {
            this._jobName       = null;
            this._gdiDevice = null;
            this._mxdwFileName  = null;
 
            _printQueue = queue;
 
            _jobName = _printQueue.CurrentJobSettings.Description;
 
            if(_jobName == null)
            {
                _jobName = NgcSerializerUtil.InferJobName(null);
            }
 
            _gdiDevice = new MetroToGdiConverter(_printQueue);
            GdiDevice.CreateDeviceContext(_jobName, InferPrintTicket());
            _isPassThruSupported = GdiDevice.ExtEscMXDWPassThru();
            GdiDevice.DeleteDeviceContext();
        }
 
        #endregion constructor
 
        public
        void
        EnablePassThru(
            )
        {
            GdiDevice.CreateDeviceContext(_jobName, InferPrintTicket());
 
            GdiDevice.ExtEscMXDWPassThru();
 
            GdiDevice.StartDocumentWithoutCreatingDC(_jobName);
 
            _mxdwFileName = GdiDevice.ExtEscGetName();
        }
 
        public
        String
        MxdwFileName
        {
            get
            {
                return _mxdwFileName;
            }
        }
 
        public
        bool
        IsPassThruSupported
        {
            get
            {
                return _isPassThruSupported;
            }
        }
 
        public
        void
        Commit(
            )
        {
            GdiDevice.EndDocument();
        }
 
        private
        PrintTicket
        InferPrintTicket(
            )
        {
            PrintTicket printTicket = new PrintTicket(_printQueue.UserPrintTicket.GetXmlStream());
 
            return printTicket;
        }
 
        private MetroToGdiConverter GdiDevice
        {
            get
            {
                return _gdiDevice;
            }
        }
 
        private
        PrintQueue              _printQueue;
 
        private
        MetroToGdiConverter     _gdiDevice;
 
        private
        String                  _jobName;
 
        private
        String                  _mxdwFileName;
 
        private
        bool                    _isPassThruSupported;
    };
 
}