//----------------------------------------------------------------------------
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//
// Description:
// Implements the FixedDocument element
//
// History:
// 06/03/2003 - Zhenbin Xu (ZhenbinX) - Created.
// 03/02/2004 - Zhenbin Xu (ZhenbinX) - Page-Per-Stream
//
// http://edocs/payloads/Payloads%20Features/FixedPage%20changes.mht
//
//---------------------------------------------------------------------------
namespace System.Windows.Documents
{
using MS.Internal; // DoubleUtil
using MS.Internal.Documents;
using MS.Utility; // ExceptionStringTable
using MS.Internal.Utility;
using System.Windows.Threading; // Dispatcher
using System.Windows; // DependencyID etc.
using System.Windows.Automation.Peers; // AutomationPeer
using System.Windows.Documents; // DocumentPaginator
using System.Windows.Documents.DocumentStructures;
using System.Windows.Media; // Visual
using System.Windows.Markup; // IAddChild, ContentPropertyAttribute
using System.Windows.Shapes; // Glyphs
using System;
using System.IO;
using System.IO.Packaging;
using System.Net;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel; // DesignerSerializationVisibility
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization.Formatters.Binary;
using MS.Internal.Annotations.Component;
using System.Windows.Navigation;
using System.Windows.Controls;
using System.Text;
using MS.Internal.PresentationFramework; //SecurityHelper
using MS.Internal.IO.Packaging;
using System.Security;
using System.Security.Permissions;
//=====================================================================
///
/// FixedDocument is the spine of a portable, high fidelity fixed-format
/// document, where the pages are stitched together. The FixedDocument
/// elements provides and formats pages as requested. It also provides
/// a Text OM on top of the fixed-format document to allow for read-only
/// editing (selection, keyboard navigation, find, etc.).
///
[ContentProperty("Pages")]
public class FixedDocument : FrameworkContentElement, IDocumentPaginatorSource, IAddChildInternal, IServiceProvider, IFixedNavigate, IUriContext
{
//-------------------------------------------------------------------
//
// Constructors
//
//----------------------------------------------------------------------
#region Constructors
static FixedDocument()
{
FocusableProperty.OverrideMetadata(typeof(FixedDocument), new FrameworkPropertyMetadata(true));
NavigationService.NavigationServiceProperty.OverrideMetadata(
typeof(FixedDocument),
new FrameworkPropertyMetadata(new PropertyChangedCallback(FixedHyperLink.OnNavigationServiceChanged)));
}
///
/// Default FixedDocument constructor
///
///
/// Automatic determination of current UIContext. Use alternative constructor
/// that accepts a UIContext for best performance.
///
public FixedDocument()
: base()
{
_Init();
}
#endregion Constructors
//-------------------------------------------------------------------
//
// Public Methods
//
//----------------------------------------------------------------------
#region IServiceProvider Members
///
/// Returns service objects associated with this control.
///
///
/// FixedDocument currently supports TextContainer.
///
/// serviceType is NULL.
///
/// Specifies the type of service object to get.
///
object IServiceProvider.GetService(Type serviceType)
{
// Dispatcher.VerifyAccess();
if (serviceType == null)
{
throw new ArgumentNullException("serviceType");
}
if (serviceType == typeof(ITextContainer))
{
return this.FixedContainer;
}
if (serviceType == typeof(RubberbandSelector))
{
// create this on demand, but not through the property, only through the
// service, so it is only created when it's actually used
if (_rubberbandSelector == null)
{
_rubberbandSelector = new RubberbandSelector();
}
return _rubberbandSelector;
}
return null;
}
#endregion IServiceProvider Members
#region IAddChild
///
/// Called to Add the object as a Child.
///
/// value is NULL.
/// value is not of type PageContent.
/// A PageContent cannot be added while previous partial page isn't completely Loaded.
///
/// Object to add as a child
///
void IAddChild.AddChild(Object value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
// Dispatcher.VerifyAccess();
PageContent fp = value as PageContent;
if (fp == null)
{
throw new ArgumentException(SR.Get(SRID.UnexpectedParameterType, value.GetType(), typeof(PageContent)), "value");
}
if (fp.IsInitialized)
{
_pages.Add(fp);
}
else
{
DocumentsTrace.FixedFormat.FixedDocument.Trace(string.Format("Page {0} Deferred", _pages.Count));
if (_partialPage == null)
{
_partialPage = fp;
_partialPage.ChangeLogicalParent(this);
_partialPage.Initialized += new EventHandler(OnPageLoaded);
}
else
{
throw new InvalidOperationException(SR.Get(SRID.PrevoiusPartialPageContentOutstanding));
}
}
}
///
/// Called when text appears under the tag in markup
///
///
/// Text to Add to the Object
///
///
void IAddChild.AddText(string text)
{
XamlSerializerUtil.ThrowIfNonWhiteSpaceInAddText(text, this);
}
#endregion
#region IUriContext
///
///
///
Uri IUriContext.BaseUri
{
get { return (Uri) GetValue(BaseUriHelper.BaseUriProperty); }
set { SetValue(BaseUriHelper.BaseUriProperty, value); }
}
#endregion IUriContext
#region IFixedNavigate
void IFixedNavigate.NavigateAsync(string elementID)
{
if (IsPageCountValid == true)
{
FixedHyperLink.NavigateToElement(this, elementID);
}
else
{
_navigateAfterPagination = true;
_navigateFragment = elementID;
}
}
UIElement IFixedNavigate.FindElementByID(string elementID, out FixedPage rootFixedPage)
{
UIElement uiElementRet = null;
rootFixedPage = null;
if (Char.IsDigit(elementID[0]))
{
//
//We convert string to a page number here.
//
int pageNumber = Convert.ToInt32(elementID, CultureInfo.InvariantCulture);
//
// Metro defines all external page are 1 based. All internals are 0 based.
//
pageNumber --;
uiElementRet = GetFixedPage(pageNumber);
rootFixedPage = GetFixedPage(pageNumber);
}
else
{
//
// We need iterate through the PageContentCollect first.
//
PageContentCollection pc = this.Pages;
PageContent pageContent;
FixedPage fixedPage;
for (int i = 0, n = pc.Count; i < n; i++)
{
pageContent = pc[i];
//
// If PageStream is non null, it is not PPS. Otherwise, need to check LinkTargets collection
// of PageContent
//
if (pageContent.PageStream != null)
{
fixedPage = GetFixedPage(i);
if (fixedPage != null)
{
uiElementRet = ((IFixedNavigate)fixedPage).FindElementByID(elementID, out rootFixedPage);
if (uiElementRet != null)
{
break;
}
}
}
else
{
if (pageContent.ContainsID(elementID))
{
fixedPage = GetFixedPage(i);
if (fixedPage != null)
{
uiElementRet = ((IFixedNavigate)fixedPage).FindElementByID(elementID, out rootFixedPage);
if (uiElementRet == null)
{ // return that page if we can't find the named fragment
uiElementRet = fixedPage;
}
//
// we always break here because pageContent include the named fragment
//
break;
}
}
}
}
}
return uiElementRet;
}
internal NavigationService NavigationService
{
get { return (NavigationService) GetValue(NavigationService.NavigationServiceProperty); }
set { SetValue(NavigationService.NavigationServiceProperty, value); }
}
#endregion
#region LogicalTree
///
/// Returns enumerator to logical children
///
protected internal override IEnumerator LogicalChildren
{
get
{
// this.Dispatcher.VerifyAccess();
return this.Pages.GetEnumerator();
}
}
#endregion LogicalTree
#region IDocumentPaginatorSource Members
///
/// An object which paginates content.
///
public DocumentPaginator DocumentPaginator
{
get { return _paginator; }
}
#endregion IDocumentPaginatorSource Members
#region Document Overrides
///
///
///
/// pageNumber is less than zero.
///
/// The page number.
///
internal DocumentPage GetPage(int pageNumber)
{
DocumentsTrace.FixedFormat.IDF.Trace(string.Format("IDP.GetPage({0})", pageNumber));
// Make sure that the call is in the right context.
// Dispatcher.VerifyAccess();
// Page number cannot be negative.
if (pageNumber < 0)
{
throw new ArgumentOutOfRangeException("pageNumber", SR.Get(SRID.IDPNegativePageNumber));
}
if (pageNumber < Pages.Count)
{
//
// If we are not out of bound, try next page
//
FixedPage page = SyncGetPage(pageNumber, false/*forceReload*/);
if (page == null)
{
return DocumentPage.Missing;
}
Debug.Assert(page != null);
Size fixedSize = ComputePageSize(page);
// Always measure with fixed size instead of using constraint
FixedDocumentPage dp = new FixedDocumentPage(this, page, fixedSize, pageNumber);
page.Measure(fixedSize);
page.Arrange(new Rect(new Point(), fixedSize));
return dp;
}
return DocumentPage.Missing;
}
///
///
///
/// pageNumber is less than zero.
/// userState is NULL.
internal void GetPageAsync(int pageNumber, object userState)
{
DocumentsTrace.FixedFormat.IDF.Trace(string.Format("IDP.GetPageAsync({0}, {1})", pageNumber, userState));
// Make sure that the call is in the right context.
// Dispatcher.VerifyAccess();
// Page number cannot be negative.
if (pageNumber < 0)
{
throw new ArgumentOutOfRangeException("pageNumber", SR.Get(SRID.IDPNegativePageNumber));
}
if (userState == null)
{
throw new ArgumentNullException("userState");
}
if (pageNumber < Pages.Count)
{
PageContent pc = Pages[pageNumber];
// Add to outstanding AsyncOp list
GetPageAsyncRequest asyncRequest = new GetPageAsyncRequest(pc, pageNumber, userState);
_asyncOps[userState] = asyncRequest;
DispatcherOperationCallback queueTask = new DispatcherOperationCallback(GetPageAsyncDelegate);
Dispatcher.BeginInvoke(DispatcherPriority.Background, queueTask, asyncRequest);
}
else
{
_NotifyGetPageAsyncCompleted(DocumentPage.Missing, pageNumber, null, false, userState);
}
}
///
///
///
/// contentPosition is NULL.
/// ContentPosition does not exist within this element?s tree.
internal int GetPageNumber(ContentPosition contentPosition)
{
// Dispatcher.VerifyAccess();
if (contentPosition == null)
{
throw new ArgumentNullException("contentPosition");
}
FixedTextPointer fixedTextPointer = contentPosition as FixedTextPointer;
if (fixedTextPointer == null)
{
throw new ArgumentException(SR.Get(SRID.IDPInvalidContentPosition));
}
return fixedTextPointer.FixedTextContainer.GetPageNumber(fixedTextPointer);
}
///
///
///
/// userState is NULL.
internal void CancelAsync(object userState)
{
DocumentsTrace.FixedFormat.IDF.Trace(string.Format("IDP.GetPageAsyncCancel([{0}])", userState));
// Dispatcher.VerifyAccess();
if (userState == null)
{
throw new ArgumentNullException("userState");
}
GetPageAsyncRequest asyncRequest;
if (_asyncOps.TryGetValue(userState,out asyncRequest))
{
if (asyncRequest != null)
{
asyncRequest.Cancelled = true;
asyncRequest.PageContent.GetPageRootAsyncCancel();
}
}
}
///
///
///
/// element is NULL.
internal ContentPosition GetObjectPosition(Object o)
{
if (o == null)
{
throw new ArgumentNullException("o");
}
DependencyObject element = o as DependencyObject;
if (element == null)
{
throw new ArgumentException(SR.Get(SRID.FixedDocumentExpectsDependencyObject));
}
DocumentsTrace.FixedFormat.IDF.Trace(string.Format("IDF.GetContentPositionForElement({0})", element));
// Make sure that the call is in the right context.
// Dispatcher.VerifyAccess();
// walk up the logical parent chain to find the containing page
FixedPage fixedPage = null;
int pageIndex = -1;
if (element != this)
{
DependencyObject el = element;
while (el != null)
{
fixedPage = el as FixedPage;
if (fixedPage != null)
{
pageIndex = GetIndexOfPage(fixedPage);
if (pageIndex >= 0)
{
break;
}
el = fixedPage.Parent;
}
else
{
el = LogicalTreeHelper.GetParent(el);
}
}
}
else if (this.Pages.Count > 0)
{
// if FixedDocument is requested, return ContentPosition for the first page.
pageIndex = 0;
}
// get FixedTextPointer for element or page index
FixedTextPointer fixedTextPointer = null;
if (pageIndex >= 0)
{
FixedPosition fixedPosition;
FlowPosition flowPosition=null;
System.Windows.Shapes.Path p = element as System.Windows.Shapes.Path;
if (element is Glyphs || element is Image || (p != null && p.Fill is ImageBrush))
{
fixedPosition = new FixedPosition(fixedPage.CreateFixedNode(pageIndex, (UIElement)element), 0);
flowPosition = FixedContainer.FixedTextBuilder.CreateFlowPosition(fixedPosition);
}
if (flowPosition == null)
{
flowPosition = FixedContainer.FixedTextBuilder.GetPageStartFlowPosition(pageIndex);
}
fixedTextPointer = new FixedTextPointer(true, LogicalDirection.Forward, flowPosition);
}
return (fixedTextPointer != null) ? fixedTextPointer : ContentPosition.Missing;
}
///
///
///
internal ContentPosition GetPagePosition(DocumentPage page)
{
FixedDocumentPage docPage = page as FixedDocumentPage;
if (docPage == null)
{
return ContentPosition.Missing;
}
return docPage.ContentPosition;
}
///
///
///
internal bool IsPageCountValid { get { return this.IsInitialized; } }
///
///
///
internal int PageCount { get { return Pages.Count; } }
///
///
///
internal Size PageSize
{
get { return new Size(_pageWidth, _pageHeight); }
set { _pageWidth = value.Width; _pageHeight = value.Height; }
}
internal bool HasExplicitStructure
{
get
{
return _hasExplicitStructure;
}
}
#endregion Document Overrides
//-------------------------------------------------------------- ------
//
// Public Properties
//
//---------------------------------------------------------------------
#region Public Properties
///
/// Get a collection of PageContent that this FixedDocument contains.
///
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
public PageContentCollection Pages
{
get
{
return _pages;
}
}
///
///
///
public static readonly DependencyProperty PrintTicketProperty
= DependencyProperty.RegisterAttached("PrintTicket", typeof(object), typeof(FixedDocument),
new FrameworkPropertyMetadata((object)null));
///
/// Get/Set PrintTicket Property
///
public object PrintTicket
{
get
{
object printTicket = GetValue(PrintTicketProperty);
return printTicket;
}
set
{
SetValue(PrintTicketProperty,value);
}
}
#endregion Public Properties
//--------------------------------------------------------------------
//
// Protected Methods
//
//---------------------------------------------------------------------
#region Protected Methods
///
/// Creates AutomationPeer ()
///
protected override AutomationPeer OnCreateAutomationPeer()
{
return new DocumentAutomationPeer(this);
}
#endregion Protected Methods
//-------------------------------------------------------------------
//
// Internal Methods
//
//---------------------------------------------------------------------
#region Internal Methods
internal int GetIndexOfPage(FixedPage p)
{
PageContentCollection pc = this.Pages;
for (int i = 0, n = pc.Count; i < n; i++)
{
if (pc[i].IsOwnerOf(p))
{
return i;
}
}
return -1;
}
internal bool IsValidPageIndex(int index)
{
return (index >= 0 && index < this.Pages.Count);
}
// Check index before trying to load page
internal FixedPage SyncGetPageWithCheck(int index)
{
if (IsValidPageIndex(index))
{
return SyncGetPage(index, false /*forceReload*/);
}
DocumentsTrace.FixedFormat.FixedDocument.Trace(string.Format("SyncGetPageWithCheck {0} is invalid page", index));
return null;
}
// Assumes index is valid
internal FixedPage SyncGetPage(int index, bool forceReload)
{
DocumentsTrace.FixedFormat.FixedDocument.Trace(string.Format("SyncGetPage {0}", index));
Debug.Assert(IsValidPageIndex(index));
PageContentCollection pc = this.Pages;
FixedPage fp;
try
{
fp = (FixedPage)pc[index].GetPageRoot(forceReload);
}
catch (Exception e)
{
if (e is InvalidOperationException || e is ApplicationException)
{
ApplicationException ae = new ApplicationException(string.Format(System.Globalization.CultureInfo.CurrentCulture, SR.Get(SRID.ExceptionInGetPage), index), e);
throw ae;
}
else
{
throw;
}
}
return fp;
}
///
/// Callback when a new PageContent is added
///
internal void OnPageContentAppended(int index)
{
FixedContainer.FixedTextBuilder.AddVirtualPage();
_paginator.NotifyPaginationProgress(new PaginationProgressEventArgs(index, 1));
if (this.IsInitialized)
{
_paginator.NotifyPagesChanged(new PagesChangedEventArgs(index, 1));
}
}
//
// Make sure page has its width and height.
// If absoluteOnly is specified, it will overwrite relative size specified
// on page with default page size.
//
internal void EnsurePageSize(FixedPage fp)
{
Debug.Assert(fp != null);
double width = fp.Width;
if (DoubleUtil.IsNaN(width))
{
fp.Width = _pageWidth;
}
double height = fp.Height;
if (DoubleUtil.IsNaN(height))
{
fp.Height = _pageHeight;
}
}
// Note: This code is specifically written for PageViewer.
// Once PageViewer get away from inquring page size before
// displaying page, we should remove this function.
internal bool GetPageSize(ref Size pageSize, int pageNumber)
{
DocumentsTrace.FixedFormat.FixedDocument.Trace(string.Format("GetPageSize {0}", pageNumber));
if (pageNumber < Pages.Count)
{
// NOTE: it is wrong to call this method when page is outstanding.
// Unfortunately PageViewer + DocumentPaginator still is dependent on
// PageSize, so have to avoid throwing exception in this situation.
FixedPage p = null;
if (!_pendingPages.Contains(Pages[pageNumber]))
{
p = SyncGetPage(pageNumber, false /*forceReload*/);
}
#if DEBUG
else
{
DocumentsTrace.FixedFormat.FixedDocument.Trace(string.Format("====== GetPageSize {0} Warning [....] call made while async outstanding =====", pageNumber));
}
#endif
// ComputePageSize will return appropriate value for null page
pageSize = ComputePageSize(p);
return true;
}
return false;
}
//
// Get absolute page size. If relative page size is specified
// in the page, it will be overwritten by default page size,
// which is absolute size.
//
internal Size ComputePageSize(FixedPage fp)
{
if (fp == null)
{
return new Size(_pageWidth, _pageHeight);
}
EnsurePageSize(fp);
return new Size(fp.Width, fp.Height);
}
#endregion Internal Methods
//--------------------------------------------------------------------
//
// Internal Properties
//
//---------------------------------------------------------------------
#region Internal Properties
// expose the hosted FixedContainer
internal FixedTextContainer FixedContainer
{
get
{
if (_fixedTextContainer == null)
{
_fixedTextContainer = new FixedTextContainer(this);
_fixedTextContainer.Highlights.Changed += new HighlightChangedEventHandler(OnHighlightChanged);
}
return _fixedTextContainer;
}
}
internal Dictionary Highlights
{
get
{
return _highlights;
}
}
internal DocumentReference DocumentReference
{
get
{
return _documentReference;
}
set
{
_documentReference = value;
}
}
#endregion Internal Properties
//--------------------------------------------------------------------
//
// Private Methods
//
//----------------------------------------------------------------------
#region Private Methods
//---------------------------------------
// Initialization
//---------------------------------------
private void _Init()
{
_paginator = new FixedDocumentPaginator(this);
_pages = new PageContentCollection(this);
_highlights = new Dictionary();
_asyncOps = new Dictionary