|
// 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 MS.Internal; // Invariant
using MS.Internal.IO.Packaging;
using System.Xml;
using System.Windows.Markup; // TypeConvertContext, ParserContext
using System.Windows.Controls; // Image
using System.Globalization; // CultureInfo
using System.Windows.Media; // ImageSource
using System.Windows.Media.Imaging; // BitmapEncoder
using System.IO; // MemoryStream
using System.IO.Packaging; // Package
using System.Threading; // Interlocked.Increment
//
// Description: Helper class for creating and accessing WPF Payloads in packages
// This file contains the definition and implementation
// for the WpfPayload class. This class acts as the
// helper for accessing the content of WPF packages.
// WPF package is a specialized structure consistent
// with Open Container Specification, so its content
// can be accessed via abstract Package class api.
// This class provides a set of convenience methods
// specific for xaml content. It allows WPF packages
// creation from avalon objects; it allows inspecting
// a package's structure without instantiating actual
// xaml content; it allows loading from WPF content
// (instantiating avalon objects).
//
namespace System.Windows.Documents
{
// An object supporting flow content packaging with images and other resources.
/// <summary>
/// WpfPayload is a class providing services for creating,
/// loading, inspecting and modifying WPF packages.
/// WPF package stands for "Windows Presentation Foundation package"
/// and used for combining sets of interrelated WPF resources
/// such as xaml files, images, fonts, ink, video etc.
/// </summary>
/// <example>
/// <para>
/// Example 1. Using WpfPayload for saving avalon objects into a single-file container.
/// </para>
/// </example>
internal class WpfPayload
{
// -------------------------------------------------------------
//
// Constants
//
// -------------------------------------------------------------
// Content types indicate type content for parts
// containing arbitrary xaml xml. Entry part of xaml package must be of such content type.
// This string is defined in the WPF Spec.
private const string XamlContentType = "application/vnd.ms-wpf.xaml+xml";
// Content types for various kinds of images
internal const string ImageBmpContentType = "image/bmp";
private const string ImageGifContentType = "image/gif";
private const string ImageJpegContentType = "image/jpeg";
private const string ImageTiffContentType = "image/tiff";
private const string ImagePngContentType = "image/png";
private const string ImageBmpFileExtension = ".bmp";
private const string ImageGifFileExtension = ".gif";
private const string ImageJpegFileExtension = ".jpeg";
private const string ImageJpgFileExtension = ".jpg";
private const string ImageTiffFileExtension = ".tiff";
private const string ImagePngFileExtension = ".png";
// Relationship uri for xaml payload entry part. The relationship is established
// between the whole package and a part representing an entry point for xaml payload.
// The reffered part is supposed to have XamlContentType content type.
// This string is defined in the WPF Spec.
private const string XamlRelationshipFromPackageToEntryPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/entry";
// Relationship uri for any secondary part of a xaml payload - images, fonts, ink, xaml pages, etc.
// This relationship is established betweeb a part with XamlContentType content type
// and a part with any other appropriate content type (such as image/png).
// This string is defined in the WPF Spec.
private const string XamlRelationshipFromXamlPartToComponentPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/component";
// To separate xaml payload from potential other content (such as fixed)
// we group all parts belonging to xaml payload under one directory.
// This is a name of this directory.
// This directory structure is not required by spec, so it is just
// a default structure. Applications loading or inspecting WPF packages
// should not make any assumptions about such directory structure.
// Using this directory though provides a good defauilt experience
// for exhcanging data between packages and regular file system:
// simply part copying creates a file structure separated from other files
// and actionable without a container.
// This string is not specified in the WPF Spec.
private const string XamlPayloadDirectory = "/Xaml"; // This directory must be available as a parameter of Save methods.
// We use this name for entry part of xaml payload.
// The application may not make any assumptions about this name
// when loading or inspecting a WPF package.
// This string is not specified in the WPF Spec.
private const string XamlEntryName = "/Document.xaml"; // This name must be available as a parameter of Save methods
// We use this name for image part of xaml payload.
// The application may not make any assumptions about this name
// when loading or inspecting a WPF package.
// This string is not specified in the WPF Spec.
private const string XamlImageName = "/Image"; // Shouldn't we use original image name instead?
// -------------------------------------------------------------
//
// Constructors
//
// -------------------------------------------------------------
// Public Constructor - initializes an instance of WpfPayload.
// A new instance of WpfPayload must be created for every copy operation.
// This instance will maintain a collection of binary resources
// needed for this act of copying. (The WpfPayload cannot be reused
// in the subsequent copying).
// The constructor is designed to be a lightweight, so it does not
// create a container yet. The instance of WpfPayload
// maintains only a list of images needed to be serialized.
// If the list is empty the container creation can be avoided,
// otherwise it can be created later by CreateContainer method call.
private WpfPayload(Package package)
{
// null package is valid value.
_package = package;
}
// -------------------------------------------------------------
//
// Public Methods
//
// -------------------------------------------------------------
/// <summary>
/// Saves the content of the range in the given stream as a WPF payload.
/// </summary>
/// <param name="range">
/// The range whose content is to be serialized.
/// </param>
/// <param name="stream">
/// When the stream is not null, it is a request to unconditionally
/// creatte WPF package in this stream.
/// If this parameter is null, then the package is created
/// only when necessary - when there are images in the range.
/// The new MemoryStream is created in this case and assigned to this
/// parameter on exit.
/// </param>
/// <param name="useFlowDocumentAsRoot">
/// </param>
/// <returns>
/// A xaml part of serialized content.
/// </returns>
internal static string SaveRange(ITextRange range, ref Stream stream, bool useFlowDocumentAsRoot)
{
return SaveRange(range, ref stream, useFlowDocumentAsRoot, false /* preserveTextElements */);
}
/// <summary>
/// Saves the content of the range in the given stream as a WPF payload.
/// </summary>
/// <param name="range">
/// The range whose content is to be serialized.
/// </param>
/// <param name="stream">
/// When the stream is not null, it is a request to unconditionally
/// creatte WPF package in this stream.
/// If this parameter is null, then the package is created
/// only when necessary - when there are images in the range.
/// The new MemoryStream is created in this case and assigned to this
/// parameter on exit.
/// </param>
/// <param name="useFlowDocumentAsRoot">
/// </param>
/// <param name="preserveTextElements">
/// If set false, custom TextElements will be upcasted to known types.
/// </param>
/// <returns>
/// A xaml part of serialized content.
/// </returns>
internal static string SaveRange(ITextRange range, ref Stream stream, bool useFlowDocumentAsRoot, bool preserveTextElements)
{
ArgumentNullException.ThrowIfNull(range);
// Create the wpf package in the stream
WpfPayload wpfPayload = new WpfPayload(/*package:*/null);
// Create a string representing serialized xaml
StringWriter stringWriter = new StringWriter(CultureInfo.InvariantCulture);
XmlTextWriter xmlWriter = new XmlTextWriter(stringWriter);
TextRangeSerialization.WriteXaml(xmlWriter, range, useFlowDocumentAsRoot, wpfPayload, preserveTextElements);
string xamlText = stringWriter.ToString();
// Decide whether we need to create a package
if (stream != null || wpfPayload._images != null)
{
// There are images in the content. Need to create a package
if (stream == null)
{
stream = new MemoryStream();
}
// Create a package in the stream
using (wpfPayload.CreatePackage(stream))
{
// Create the entry part for xaml content of the WPF package
PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart();
// Write the part's content
Stream xamlPartStream = xamlEntryPart.GetSeekableStream();
using (xamlPartStream)
{
StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream);
using (xamlPartWriter)
{
xamlPartWriter.Write(xamlText);
}
}
// Write relationships from xaml entry part to all images
wpfPayload.CreateComponentParts(xamlEntryPart);
}
Invariant.Assert(wpfPayload._images == null); // must have beed cleared in CreateComponentParts
}
return xamlText;
}
// Creates a WPF container in new MemoryStream and places an image into it
// with the simplest xaml part referring to it (wrapped into InlineUIContainer).
internal static MemoryStream SaveImage(BitmapSource bitmapSource, string imageContentType)
{
MemoryStream stream = new MemoryStream();
// Create the wpf package in the stream
WpfPayload wpfPayload = new WpfPayload(/*package:*/null);
// Create a package in the stream
using (wpfPayload.CreatePackage(stream))
{
// Define a reference for the image
int imageIndex = 0;
string imageReference = GetImageReference(GetImageName(imageIndex, imageContentType));
// Create the entry part for xaml content of the WPF package
PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart();
// Write the part's content
Stream xamlPartStream = xamlEntryPart.GetSeekableStream();
using (xamlPartStream)
{
StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream);
using (xamlPartWriter)
{
string xamlText =
$"<Span xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"><InlineUIContainer><Image Width=\"{bitmapSource.Width}\" Height=\"{bitmapSource.Height}\" ><Image.Source><BitmapImage CacheOption=\"OnLoad\" UriSource=\"{imageReference}\"/></Image.Source></Image></InlineUIContainer></Span>";
xamlPartWriter.Write(xamlText);
}
}
// Add image to a package
wpfPayload.CreateImagePart(xamlEntryPart, bitmapSource, imageContentType, imageIndex);
}
return stream;
}
/// <summary>
/// Loads xaml content from a WPF package.
/// </summary>
/// <param name="stream">
/// Stream that must be accessible for reading and structured as
/// a WPF container: part XamlEntryPart is expected as one of
/// its entry parts.
/// </param>
/// <returns>
/// Returns a xaml element loaded from the entry part of the package.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Throws parsing exception when the xaml content does not comply with the xaml schema.
/// </exception>
/// <exception cref="ArgumentException">
/// Throws validation exception when the package is not well structured.
/// </exception>
/// <exception cref="Exception">
/// Throws uri exception when the pachageBaseUri is not correct absolute uri.
/// </exception>
/// <remarks>
/// USED IN LEXICON VIA REFLECTION
/// </remarks>
internal static object LoadElement(Stream stream)
{
ArgumentNullException.ThrowIfNull(stream);
object xamlObject;
try
{
WpfPayload wpfPayload = WpfPayload.OpenWpfPayload(stream);
// Now load the package
using (wpfPayload.Package)
{
// Validate WPF paypoad and get its entry part
PackagePart xamlEntryPart = wpfPayload.ValidatePayload();
// Define a unique uri for this instance of PWF payload.
// Uniqueness is required to make sure that cached images are not mixed up.
int newWpfPayoutCount = Interlocked.Increment(ref _wpfPayloadCount);
Uri payloadUri = new Uri($"payload://wpf{newWpfPayoutCount}", UriKind.Absolute);
Uri entryPartUri = System.IO.Packaging.PackUriHelper.Create(payloadUri, xamlEntryPart.Uri); // gives an absolute uri of the entry part
Uri packageUri = System.IO.Packaging.PackUriHelper.GetPackageUri(entryPartUri); // extracts package uri from combined package+part uri
PackageStore.AddPackage(packageUri, wpfPayload.Package); // Register the package
// Set this temporary uri as a base uri for xaml parser
ParserContext parserContext = new ParserContext();
parserContext.BaseUri = entryPartUri;
// Call xaml parser
xamlObject = XamlReader.Load(xamlEntryPart.GetSeekableStream(), parserContext, useRestrictiveXamlReader: true);
// Remove the temporary uri from the PackageStore
PackageStore.RemovePackage(packageUri);
}
}
catch (XamlParseException e)
{
// Incase of xaml parsing or package structure failure
// we return null.
Invariant.Assert(e != null); //to make compiler happy about not using a variable e. This variable is useful in debugging process though - to see a reason of a parsing failure
xamlObject = null;
}
catch (System.IO.FileFormatException)
{
xamlObject = null;
}
catch (System.IO.FileLoadException)
{
xamlObject = null;
}
catch (System.OutOfMemoryException)
{
xamlObject = null;
}
return xamlObject;
}
// Checks whether the WPF payload meets minimal structural requirements:
// 1) Entry part exists
// 2) All components have proper relationships established between source and target
// Returns an entry part of the payload if it is valid; throws otherwise.
private PackagePart ValidatePayload()
{
// Get the WPF entry part
PackagePart xamlEntryPart = this.GetWpfEntryPart();
if (xamlEntryPart == null)
{
throw new XamlParseException(SR.TextEditorCopyPaste_EntryPartIsMissingInXamlPackage);
}
// Add more validation for package structure
return xamlEntryPart;
}
static int _wpfPayloadCount; // used to disambiguate between all acts of loading from different WPF payloads.
// -------------------------------------------------------------
//
// Public Properties
//
// -------------------------------------------------------------
#region Public Properties
/// <summary>
/// Returns a Package containing this WpfPayload.
/// </summary>
public Package Package
{
get
{
return _package;
}
}
#endregion Public Properties
// -------------------------------------------------------------
//
// Internal Methods
//
// -------------------------------------------------------------
/// <summary>
/// Gets a BitmapSource from an Image. In the case of a DrawingImage, we must first render
/// to an offscreen bitmap since the DrawingImage's previously rendered bits are not kept
/// in memory.
/// </summary>
private BitmapSource GetBitmapSourceFromImage(Image image)
{
if (image.Source is BitmapSource)
{
return (BitmapSource)image.Source;
}
Invariant.Assert(image.Source is DrawingImage);
DpiScale dpi = image.GetDpi();
DrawingImage di = (DrawingImage)image.Source;
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(di.Width * dpi.DpiScaleX), (int)(di.Height * dpi.DpiScaleY),
96.0, 96.0, PixelFormats.Default);
rtb.Render(image);
return rtb;
}
// Creates relationships from the given part to all images currently stored in _images array.
// This method is supposed to be called at the end of each sourcePart processing
// when _images array still contains a list of all images referenced from this part.
private void CreateComponentParts(PackagePart sourcePart)
{
if (_images != null)
{
for (int imageIndex = 0; imageIndex < _images.Count; imageIndex++)
{
Image image = _images[imageIndex];
// Define image type
string imageContentType = GetImageContentType(image.Source.ToString());
CreateImagePart(sourcePart, GetBitmapSourceFromImage(image), imageContentType, imageIndex);
}
// Clear _images array - to avoid the temptation of re-usinng it anymore.
_images = null;
}
}
// Creates a part containing an image with a relationship to it from a sourcePart
private void CreateImagePart(PackagePart sourcePart, BitmapSource imageSource, string imageContentType, int imageIndex)
{
// Generate a new unique image part name
string imagePartUriString = GetImageName(imageIndex, imageContentType);
// Define an image part uri
Uri imagePartUri = new Uri(XamlPayloadDirectory + imagePartUriString, UriKind.Relative);
// Create a part for the image
PackagePart imagePart = _package.CreatePart(imagePartUri, imageContentType, CompressionOption.NotCompressed);
// Create the relationship referring from the enrty part to the image part
PackageRelationship componentRelationship = sourcePart.CreateRelationship(imagePartUri, TargetMode.Internal, XamlRelationshipFromXamlPartToComponentPart);
// Encode the image data
BitmapEncoder bitmapEncoder = GetBitmapEncoder(imageContentType);
bitmapEncoder.Frames.Add(BitmapFrame.Create(imageSource));
// Save encoded image data into the image part in the package
Stream imageStream = imagePart.GetSeekableStream();
using (imageStream)
{
bitmapEncoder.Save(imageStream);
}
}
// Adds an image data to the package.
// Returns a local Uri that must be used to access this data
// from the package - from its top level directory.
internal string AddImage(Image image)
{
ArgumentNullException.ThrowIfNull(image);
if (image.Source == null)
{
throw new ArgumentNullException("image.Source");
}
if (string.IsNullOrEmpty(image.Source.ToString()))
{
throw new ArgumentException(SR.WpfPayload_InvalidImageSource);
}
if (_images == null)
{
_images = new List<Image>();
}
// Define the image uri for the new image
string imagePartUriString = null;
// Define image type
string imageContentType = GetImageContentType(image.Source.ToString());
// Check whether we already have the image with the same BitmapFrame
for (int i = 0; i < _images.Count; i++)
{
if (ImagesAreIdentical(GetBitmapSourceFromImage(_images[i]), GetBitmapSourceFromImage(image)))
{
// Image content types must be consistent
Invariant.Assert(imageContentType == GetImageContentType(_images[i].Source.ToString()), $"Image content types expected to be consistent: {imageContentType} vs. {GetImageContentType(_images[i].Source.ToString())}");
// We have this image registered already. Return its part uri
imagePartUriString = GetImageName(i, imageContentType);
}
}
// If this is new unique image, add it to our collection
if (imagePartUriString == null)
{
// Generate a new unique image part name
imagePartUriString = GetImageName(_images.Count, imageContentType);
_images.Add(image); // this will change _images.Count used for generating image parts names
}
// Return the image Part Uri for xaml serializer to use as Image.Source attribute
return GetImageReference(imagePartUriString);
}
// Parses the imageUriString to identify its content type.
// The decision is made based on file extension.
// When file extension is not recognized, image/png is choosen.
private static string GetImageContentType(string imageUriString)
{
string imageContentType;
if (imageUriString.EndsWith(ImageBmpFileExtension, StringComparison.OrdinalIgnoreCase))
{
imageContentType = ImageBmpContentType;
}
else if (imageUriString.EndsWith(ImageGifFileExtension, StringComparison.OrdinalIgnoreCase))
{
imageContentType = ImageGifContentType;
}
else if (imageUriString.EndsWith(ImageJpegFileExtension, StringComparison.OrdinalIgnoreCase) || imageUriString.EndsWith(ImageJpgFileExtension, StringComparison.OrdinalIgnoreCase))
{
imageContentType = ImageJpegContentType;
}
else if (imageUriString.EndsWith(ImageTiffFileExtension, StringComparison.OrdinalIgnoreCase))
{
imageContentType = ImageTiffContentType;
}
else
{
imageContentType = ImagePngContentType;
}
return imageContentType;
}
// Returns a BitmapEncoder corresponding to a given imageContentType
private static BitmapEncoder GetBitmapEncoder(string imageContentType)
{
BitmapEncoder bitmapEncoder;
switch (imageContentType)
{
case ImageBmpContentType:
bitmapEncoder = new BmpBitmapEncoder();
break;
case ImageGifContentType:
bitmapEncoder = new GifBitmapEncoder();
break;
case ImageJpegContentType:
bitmapEncoder = new JpegBitmapEncoder();
// investigate crash when qualitylevel is set to 100.
//((JpegBitmapEncoder)bitmapEncoder).QualityLevel = 100; // To minimize data loss; default is 75
break;
case ImageTiffContentType:
bitmapEncoder = new TiffBitmapEncoder();
break;
case ImagePngContentType:
bitmapEncoder = new PngBitmapEncoder();
break;
default:
Invariant.Assert(false, $"Unexpected image content type: {imageContentType}");
bitmapEncoder = null;
break;
}
return bitmapEncoder;
}
// Returns a file extension corresponding to a given imageContentType
private static string GetImageFileExtension(string imageContentType)
{
string imageFileExtension;
switch (imageContentType)
{
case ImageBmpContentType:
imageFileExtension = ImageBmpFileExtension;
break;
case ImageGifContentType:
imageFileExtension = ImageGifFileExtension;
break;
case ImageJpegContentType:
imageFileExtension = ImageJpegFileExtension;
break;
case ImageTiffContentType :
imageFileExtension = ImageTiffFileExtension;
break;
case ImagePngContentType:
imageFileExtension = ImagePngFileExtension;
break;
default:
Invariant.Assert(false, $"Unexpected image content type: {imageContentType}");
imageFileExtension = null;
break;
}
return imageFileExtension;
}
// Returns true if image bitmap data in memory aree the same instance for the both images
private static bool ImagesAreIdentical(BitmapSource imageSource1, BitmapSource imageSource2)
{
// First compare images as objects - the luckiest case is when it's the same object
BitmapFrameDecode imageBitmap1 = imageSource1 as BitmapFrameDecode;
BitmapFrameDecode imageBitmap2 = imageSource2 as BitmapFrameDecode;
if (imageBitmap1 != null && imageBitmap2 != null &&
imageBitmap1.Decoder.Frames.Count == 1 && imageBitmap2.Decoder.Frames.Count == 1 &&
imageBitmap1.Decoder.Frames[0] == imageBitmap2.Decoder.Frames[0])
{
return true; // ImageSources have the same instance of bitmap data. They are obviousely identical.
}
if (imageSource1.Format.BitsPerPixel != imageSource2.Format.BitsPerPixel ||
imageSource1.PixelWidth != imageSource2.PixelWidth ||
imageSource1.PixelHeight != imageSource2.PixelHeight ||
imageSource1.DpiX != imageSource2.DpiX ||
imageSource1.DpiY != imageSource2.DpiY ||
imageSource1.Palette != imageSource2.Palette)
{
return false; // Images have different characteristics
}
int stride = ((imageSource1.PixelWidth * imageSource1.Format.BitsPerPixel) + 7) / 8;
int bufferSize = (stride * (imageSource1.PixelHeight - 1)) + stride;
Byte[] buffer1 = new Byte[bufferSize];
Byte[] buffer2 = new Byte[bufferSize];
imageSource1.CopyPixels(buffer1, stride, /*offset:*/0);
imageSource2.CopyPixels(buffer2, stride, /*offset:*/0);
for (int i = 0; i < bufferSize; i++)
{
if (buffer1[i] != buffer2[i])
{
return false; // Images have different pixels
}
}
return true; // Images are equal
}
// ------------------------------------
// API needed for RTF-to-XAML Converter
// ------------------------------------
internal Stream CreateXamlStream()
{
PackagePart part = this.CreateWpfEntryPart();
// Return a stream opened for writing an image data
return part.GetSeekableStream();
}
internal Stream CreateImageStream(int imageCount, string contentType, out string imagePartUriString)
{
// Generate a new unique image part name
imagePartUriString = GetImageName(imageCount, contentType);
// Add image part to the conntainer
// Define an image part uri
Uri imagePartUri = new Uri(XamlPayloadDirectory + imagePartUriString, UriKind.Relative);
// Create a part for the image
PackagePart imagePart = _package.CreatePart(imagePartUri, contentType, CompressionOption.NotCompressed);
// Create the relationship referring from the enrty part to the image part
//PackageRelationship entryRelationship = _currentXamlPart.CreateRelationship(imagePartUri, TargetMode.Internal, XamlRelationshipFromXamlPartToComponentPart);
// Return relative name for the image part as out parameter
imagePartUriString = GetImageReference(imagePartUriString);
// Return a stream opened for writing an image data
return imagePart.GetSeekableStream();
}
internal Stream GetImageStream(string imageSourceString)
{
Invariant.Assert(imageSourceString.StartsWith("./", StringComparison.OrdinalIgnoreCase));
imageSourceString = imageSourceString.Substring(1); // cut the leading dot out
Uri imagePartUri = new Uri(XamlPayloadDirectory + imageSourceString, UriKind.Relative);
PackagePart imagePart = _package.GetPart(imagePartUri);
return imagePart.GetSeekableStream();
}
// -------------------------------------------------------------
//
// Private Methods
//
// -------------------------------------------------------------
private Package CreatePackage(Stream stream)
{
Invariant.Assert(_package == null, "Package has been already created or open for this WpfPayload");
_package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
return _package;
}
/// <summary>
/// Creates an instance of WpfPayload object.
/// </summary>
/// <param name="stream">
/// A stream where the package for this wpf payload is contained
/// </param>
/// <returns>
/// Returns an instance of WpfPayload.
/// </returns>
/// <remarks>
/// The instance of WpfPayload created by this method is supposed
/// to be disposed later (IDispose.Dispose()) or closed by calling
/// the Close method - to flush all changes to a persistent storage
/// and free all temporary resources.
/// </remarks>
internal static WpfPayload CreateWpfPayload(Stream stream)
{
Package package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
return new WpfPayload(package);
}
/// <summary>
/// Creates an instance of WpfPayload object.
/// </summary>
/// <param name="stream">
/// A stream where the package for this wpf payload is contained
/// </param>
/// <returns>
/// Returns an instance of WpfPayload.
/// </returns>
/// <remarks>
/// The instance of WpfPayload created by this method is supposed
/// to be disposed later (IDispose.Dispose()) or closed by calling
/// the Close method - to flush all changes to a persistent storage
/// and free all temporary resources.
/// </remarks>
internal static WpfPayload OpenWpfPayload(Stream stream)
{
Package package = Package.Open(stream, FileMode.Open, FileAccess.Read);
return new WpfPayload(package);
}
private PackagePart CreateWpfEntryPart()
{
// Define an entry part uri
Uri entryPartUri = new Uri(XamlPayloadDirectory + XamlEntryName, UriKind.Relative);
// Create the main xaml part
PackagePart part = _package.CreatePart(entryPartUri, XamlContentType, CompressionOption.Normal);
// Compression is turned off in this mode.
//NotCompressed = -1,
// Compression is optimized for a resonable compromise between size and performance.
//Normal = 0,
// Compression is optimized for size.
//Maximum = 1,
// Compression is optimized for performance.
//Fast = 2 ,
// Compression is optimized for super performance.
//SuperFast = 3,
// Create the relationship referring to the entry part
PackageRelationship entryRelationship = _package.CreateRelationship(entryPartUri, TargetMode.Internal, XamlRelationshipFromPackageToEntryPart);
return part;
}
/// <summary>
/// Retrieves an entry part marked as a WPF payload entry part
/// by appropriate package relationship.
/// </summary>
/// <returns>
/// PackagePart containing a Wpf package entry.
/// Null if such part does not exist in this package.
/// </returns>
private PackagePart GetWpfEntryPart()
{
PackagePart wpfEntryPart = null;
// Find a relationship to entry part
PackageRelationshipCollection entryPartRelationships = _package.GetRelationshipsByType(XamlRelationshipFromPackageToEntryPart);
PackageRelationship entryPartRelationship = null;
foreach (PackageRelationship packageRelationship in entryPartRelationships)
{
entryPartRelationship = packageRelationship;
break;
}
// Get a part referred by this relationship
if (entryPartRelationship != null)
{
// Get entry part uri
Uri entryPartUri = entryPartRelationship.TargetUri;
// Get the enrty part
wpfEntryPart = _package.GetPart(entryPartUri);
}
return wpfEntryPart;
}
// Generates a image part Uri for the given image index
private static string GetImageName(int imageIndex, string imageContentType)
{
string imageFileExtension = GetImageFileExtension(imageContentType);
return XamlImageName + (imageIndex + 1) + imageFileExtension;
}
// Generates a relative URL for using from within xaml Image tag.
private static string GetImageReference(string imageName)
{
return $".{imageName}"; // imageName is supposed to be created by GetImageName method
}
// -------------------------------------------------------------
//
// Private Fields
//
// -------------------------------------------------------------
#region Private Fields
// Package used as a storage for thow WPF container
private Package _package;
// Hashtable of images added to a package so far.
// Used during xaml serialization intended for adding as a part of the WPF package.
private List<Image> _images;
#endregion Private Fields
}
}
|