Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Print / Reach / Serialization / VisualSerializer.cs / 1 / VisualSerializer.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, all rights reserved // // File: VisualSerializer.cs // // History: // [....]: 10/07/2003 Created // [....]: 03/02/2004 Adding HwndVisual support (DrawingVisual / EMF / Bitmap) // [....]: 04/13/2004 Refactor to spec //avalon/medialayer/Specifications/Visual%20Serialization.mht // [....]: 12/08/2004 Code to new spec //d2/C2/DevTeam/SharedDocuments/Reach%20Visual%20to%20S0%20Serializer.mht // //----------------------------------------------------------------------------- using System; using System.Diagnostics; using System.Collections; using System.Collections.Specialized; using System.Collections.Generic; using System.Globalization; using System.Text; using System.IO; using System.Xml; using System.ComponentModel; using System.Windows; using System.Windows.Automation; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Xps.Packaging; using Microsoft.Internal.AlphaFlattener; using System.Security; using System.Security.Permissions; namespace System.Windows.Xps.Serialization { #region class VisualSerializer ////// Visual Serializer /// internal class VisualSerializer: IMetroDrawingContext { public const double PrecisionDPI = 9600; // Numeric values generated should be at least precise to that resolution // XPS Specification and Reference Guide // 11.2 Implementation Limits // Producers SHOULD produce only XPS Documents that stay within these implementation limits public const double PositiveLargestFloat = 1e38; public const double NegativeLargestFloat = -1e38; public const double PositiveSmallestFloat = 2e-38; // spec says 1e-38, but it's not representable in float public const double NegativeSmallestFloat = -2e-38; public const int MaxElementCount = 1000 * 1000; public const int MaxPointCount = 100 * 1000; public const int MaxResourceCount = 10 * 1000; public const int MaxGlyphCount = 5 * 1000; public const int MaxGradientStops = 100; #region Constructor ////// Constructor for DrawingContext which accepts the context with which this /// instance should be affiliated. /// /// XmlWriter for resource /// XmlWriter for body /// internal VisualSerializer(System.Xml.XmlWriter resWriter, System.Xml.XmlWriter bodyWriter, PackageSerializationManager manager) { _objects = new ArrayList(); _objnams = new ArrayList(); _resWriter = resWriter; _bodyWriter = bodyWriter; _manager = manager; if (_manager != null) { _context = new XpsTokenContext(_manager, null, null); } _writer = _bodyWriter; _tcoStack = new Stack(); SetCoordinateFormat(1); } #endregion Constructor #region protected methods protected double CheckFloat(double v) { if (v > 0) { if (v < PositiveSmallestFloat) { return 0; } if (v > PositiveLargestFloat) { _exceedFloatLimit = true; } } else if (v < 0) { if (v > NegativeSmallestFloat) { return 0; } if (v < NegativeLargestFloat) { _exceedFloatLimit = true; } } return v; } void ReportLimitViolation() { if (_exceedFloatLimit) { _writer.WriteComment("XPSLimit:FloatRange"); _exceedFloatLimit = false; } if (_exceedPointLimit) { _writer.WriteComment("XPSLimit:PointCount"); _exceedPointLimit = false; } } protected void AppendCoordinate(StringBuilder rslt, double v) { v = CheckFloat(v); if (_forceGeneral > 0) // precision control ignored { rslt.Append(v.ToString(CultureInfo.InvariantCulture)); } else { rslt.Append(v.ToString(_coordFormat, CultureInfo.InvariantCulture)); } } protected void AppendPoint(StringBuilder builder, Point p, Matrix mat) { if (!mat.IsIdentity) { p = mat.Transform(p); } AppendCoordinate(builder, p.X); builder.Append(','); AppendCoordinate(builder, p.Y); } protected int AppendPoints(StringBuilder builder, PointCollection pc, Matrix mat) { bool first = true; foreach (Point p in pc) { if (first) { first = false; } else { builder.Append(' '); } AppendPoint(builder, p, mat); } return pc.Count; } protected string GetString(object obj) { if (obj == null) { return null; } StringBuilder rslt = new StringBuilder(); while (true) { DoubleCollection dc = obj as DoubleCollection; if (dc != null) { foreach (double d in dc) { if (rslt.Length != 0) { rslt.Append(' '); } rslt.Append(CheckFloat(d).ToString(CultureInfo.InvariantCulture)); } break; } PointCollection pc = obj as PointCollection; if (pc != null) { foreach (Point p in pc) { if (rslt.Length != 0) { rslt.Append(' '); } AppendPoint(rslt, p, Matrix.Identity); } break; } if (obj is Matrix) { AppendMatrix(rslt, (Matrix)obj); } else if (obj is Point) { AppendPoint(rslt, (Point)obj, Matrix.Identity); } else if (obj is double) { Double d = CheckFloat((double) obj); rslt.Append(d.ToString(CultureInfo.InvariantCulture)); } else if (obj is Uri) { rslt.Append(GetUriAsString((Uri)obj)); } else { rslt.Append(Convert.ToString(obj, CultureInfo.InvariantCulture)); } break; } if (rslt.Length == 0) { return null; } else { return rslt.ToString(); } } ////// Search for cached brush /// protected string FindBrush(Brush brush, Rect bounds) { Debug.Assert(!BrushProxy.IsEmpty(brush), "Should not be serializing empty brush"); StringBuilder sbBrush; _forceGeneral++; try { sbBrush = BrushToString(brush, bounds); } finally { _forceGeneral--; } string sBrush = sbBrush.ToString(); for (int i = 0; i < _objects.Count; i++) { if ((string) _objects[i] == sBrush) { return _objnams[i] as string; } } _objects.Add(sBrush); _objnams.Add("b" + _brushId); // Replace brush ID place holder with the real ID: // Can't put ID in for brush reuse. // Can't replace within the whole StringBuilder because of possible appearance of bxx in GlyphRun //// sbBrush = sbBrush.Replace("bxx", "b" + _brushId, 0, 40); _brushId++; _resWriter.WriteRaw(sbBrush.ToString()); _resWriter.WriteWhitespace("\r\n"); return _objnams[_objects.Count - 1] as string; } // Output an object protected void WriteAttr(string attribute, object val) { _writer.WriteAttributeString(attribute, GetString(val)); } // Output an object if it's not the same as default protected void WriteAttr(string attribute, object val, object valDefault) { string sval = GetString(val); if (sval != GetString(valDefault)) { _writer.WriteAttributeString(attribute, sval); } } protected string ColorToString(Color color) { string colorString = "#00FFFFFF"; TypeConverter converter = _manager.GetTypeConverter(typeof(Color)); if (converter != null) { colorString = converter.ConvertTo(_context, CultureInfo.InvariantCulture, color, typeof(string)) as string; } return colorString; } // Convert simple brush to inline string protected string SimpleBrushToString(Brush brush) { SolidColorBrush solidBrush = brush as SolidColorBrush; if (solidBrush != null) // SolidColorBrush { // Scale will normalize colors Color color = Utility.Scale(solidBrush.Color, solidBrush.Opacity); return ColorToString(color); } return null; } // Write GradientStopCollection protected void WriteGradientStops(string prefix, GradientStopCollection gsc) { _writer.WriteStartElement(prefix + ".GradientStops"); int count = gsc.Count; if (count > 0) { bool[] taken = new bool[count]; // Sort gradientstops according to offsets, without changing order of stops with the same offset for (int i = 0; i < count; i++) { int pos = -1; double val = double.MaxValue; // Find the first, free, smallest offset for (int j = count - 1; j >= 0; j--) { if (!taken[j]) { // treat NaN offset as MaxValue when sorting double offset = gsc[j].Offset; if (double.IsNaN(offset)) { offset = double.MaxValue; } if (offset <= val) { pos = j; val = offset; } } } Debug.Assert(pos >= 0, "Missing offset"); GradientStop stop = gsc[pos]; taken[pos] = true; _writer.WriteStartElement("GradientStop"); WriteAttr("Color", ColorToString(stop.Color)); WriteAttr("Offset", stop.Offset); _writer.WriteEndElement(); } } _writer.WriteEndElement(); if (count > MaxGradientStops) { _writer.WriteComment("XPSLimit:GradientStopCount"); } } private void WriteBrushHeader(string element, Brush brush) { _writer.WriteStartElement(element); WriteAttr("x:Key", "bxx"); WriteAttr("Opacity", brush.Opacity, 1.0); } static Rect UnitRect = new Rect(0, 0, 1, 1); private void WriteTileBrush(string element, TileBrush brush, Rect bounds) { WriteBrushHeader(element, brush); BrushMappingMode mapmode = brush.ViewportUnits; if (mapmode == BrushMappingMode.Absolute) { bounds = UnitRect; } mapmode = BrushMappingMode.Absolute; WriteAttr("ViewportUnits", mapmode); WriteAttr("TileMode", brush.TileMode); // Remove AlignmentX/AlignmentY. // Or more precisely, change Viewbox/ViewPort so center alignment would replace current setting double dstwidth = brush.Viewport.Width * bounds.Width; double dstheight = brush.Viewport.Height * bounds.Height; Rect vb = Utility.GetTileAbsoluteViewbox(brush); double srcwidth = vb.Width; double srcheight = vb.Height; double scalex; double scaley; bool adjustViewport = true; // viewport switch (brush.Stretch) { case Stretch.None: scalex = 1; scaley = 1; if (srcwidth > dstwidth || srcheight > dstheight) { // // Fix bug 1326548: XPS serialization: Incorrect viewbox value in S0 after ImageBrush conversion // // Incorrect TileBrush serialization when Stretch.None, content larger than target bounds, // and alignment specified. Need to adjust viewbox to handle alignment, not viewport. // adjustViewport = false; } break; case Stretch.Uniform: scalex = Math.Min(dstwidth / srcwidth, dstheight / srcheight); scaley = scalex; break; case Stretch.UniformToFill: scalex = Math.Max(dstwidth / srcwidth, dstheight / srcheight); scaley = scalex; // UniformToFill normally maps Viewbox to an area which is larger than Viewport. // So we need to adjust Viewbox to take care of alignment adjustViewport = false; // need to adjust viewbox break; case Stretch.Fill: default: scalex = dstwidth / srcwidth; scaley = dstheight / srcheight; break; } double width = srcwidth * scalex; double height = srcheight * scaley; double dx, dy; // Calculate differeences between current alignments and the default center alignment switch (brush.AlignmentX) { case AlignmentX.Left: dx = - (dstwidth - width) / 2; break; case AlignmentX.Right: dx = (dstwidth - width) / 2; break; case AlignmentX.Center: default: dx = 0; break; } switch (brush.AlignmentY) { case AlignmentY.Top: dy = - (dstheight - height) / 2; break; case AlignmentY.Bottom: dy = (dstheight - height) / 2; break; case AlignmentY.Center: default: dy = 0; break; } //WriteAttr("AlignmentX", brush.AlignmentX); //WriteAttr("AlignmentY", brush.AlignmentY); WriteAttr("ViewboxUnits", BrushMappingMode.Absolute); Rect vp = brush.Viewport; vp = new Rect(bounds.X + vp.X * bounds.Width, bounds.Y + vp.Y * bounds.Height, vp.Width * bounds.Width, vp.Height * bounds.Height); if (adjustViewport) { vp = new Rect(vp.Left + dx, vp.Top + dy, vp.Width, vp.Height); } else { // Change direction and scale to viewbox coordinate space vb = new Rect(vb.Left - dx / scalex, vb.Top - dy / scaley, vb.Width, vb.Height); } // Adjusting Viewbox so that Stretch can be changed to "Fill" // Calculate real width/height being used in stretching double w1 = vp.Width / scalex; double h1 = vp.Height / scaley; vb = new Rect(vb.Left + (vb.Width - w1) / 2, // center alignment, could be negative vb.Top + (vb.Height - h1) / 2, // center alignment, could be negative w1, h1); WriteAttr("Viewbox", vb.ToString(CultureInfo.InvariantCulture)); WriteAttr("Viewport", vp.ToString(CultureInfo.InvariantCulture)); } void SaveResetState() { // Save critical state info _tcoStack.Push(_opacity); _tcoStack.Push(_opacityMask); _tcoStack.Push(_transform); _tcoStack.Push(_clip); // Reset critical state info _opacity = 1; _opacityMask = null; _transform = null; _clip = null; } void RestoreState() { // Restore critical state info _clip = _tcoStack.Pop() as Geometry; _transform = _tcoStack.Pop() as Transform; _opacityMask = _tcoStack.Pop() as Brush; _opacity = (double)_tcoStack.Pop(); } // Output primitives in drawing private void WriteDrawingBody(System.Windows.Media.Drawing drawing, Matrix worldTransform) { SaveResetState(); _writer.WriteStartElement("Canvas"); VisualTreeFlattener vtf = new VisualTreeFlattener(this, _pageSize); vtf.DrawingWalk(drawing, worldTransform); _writer.WriteEndElement(); RestoreState(); } protected void WriteBitmap(string attribute, ImageSource imageSource) { if ((imageSource != null) && (imageSource.Height > 0) && (imageSource.Width > 0)) { string bitmapUri = null; TypeConverter converter = _manager.GetTypeConverter(typeof(BitmapSource)); if (converter != null) { ColorConvertedBitmap colorConvertedBitmap = imageSource as ColorConvertedBitmap; if ( colorConvertedBitmap!=null ) { if (colorConvertedBitmap.Source is FormatConvertedBitmap) { FormatConvertedBitmap formatConvertedBitmap = colorConvertedBitmap.Source as FormatConvertedBitmap; if (formatConvertedBitmap.Source is BitmapFrame) { imageSource = formatConvertedBitmap.Source; } else { colorConvertedBitmap = null; } } else if (colorConvertedBitmap.Source is BitmapFrame) { imageSource = colorConvertedBitmap.Source; } else { colorConvertedBitmap = null; } } Object obj = converter.ConvertTo(_context, null, imageSource, typeof(Uri)); Uri uri = obj as Uri; if (uri != null) { bitmapUri = GetUriAsString(uri); } else { bitmapUri = obj as String; } if (colorConvertedBitmap != null) { string sourceProfile = ColorTypeConverter.SerializeColorContext(_context,colorConvertedBitmap.SourceColorContext); bitmapUri = "{ColorConvertedBitmap " + bitmapUri + " " + sourceProfile; if (new ColorContext(PixelFormats.Default) != colorConvertedBitmap.DestinationColorContext) { string destinationProfile = ColorTypeConverter.SerializeColorContext(_context, colorConvertedBitmap.DestinationColorContext); bitmapUri = bitmapUri + " " + destinationProfile; } bitmapUri = bitmapUri + "}"; } } WriteAttr(attribute, bitmapUri); } } // Convert a brush to a StringBuilder protected StringBuilder BrushToString(Brush brush, Rect bounds) { StringWriter swriter = new StringWriter(CultureInfo.InvariantCulture); XmlTextWriter xwriter = new XmlTextWriter(swriter); xwriter.Formatting = System.Xml.Formatting.Indented; xwriter.Indentation = 4; XmlWriter oldwriter = _writer; _writer = xwriter; while (true) { SolidColorBrush sb = brush as SolidColorBrush; if (sb != null) // SolidColorBrush { WriteBrushHeader("SolidColorBrush", sb); WriteAttr("Color", sb.Color); break; } LinearGradientBrush lb = brush as LinearGradientBrush; if (lb != null) { WriteBrushHeader("LinearGradientBrush", lb); WriteTransform("Transform", lb.Transform, lb.RelativeTransform, bounds); BrushMappingMode mapmode = lb.MappingMode; if (mapmode == BrushMappingMode.Absolute) { bounds = UnitRect; } mapmode = BrushMappingMode.Absolute; WriteAttr("StartPoint", Utility.MapPoint(bounds, lb.StartPoint)); WriteAttr("EndPoint", Utility.MapPoint(bounds, lb.EndPoint)); WriteAttr("ColorInterpolationMode", lb.ColorInterpolationMode); WriteAttr("MappingMode", mapmode); WriteAttr("SpreadMethod", lb.SpreadMethod); WriteGradientStops("LinearGradientBrush", lb.GradientStops); break; } RadialGradientBrush rb = brush as RadialGradientBrush; if (rb != null) { WriteBrushHeader("RadialGradientBrush", rb); WriteTransform("Transform", rb.Transform, rb.RelativeTransform, bounds); BrushMappingMode mapmode = rb.MappingMode; if (mapmode == BrushMappingMode.Absolute) { bounds = UnitRect; } mapmode = BrushMappingMode.Absolute; WriteAttr("MappingMode", mapmode); WriteAttr("SpreadMethod", rb.SpreadMethod); WriteAttr("ColorInterpolationMode", rb.ColorInterpolationMode); WriteAttr("Center", Utility.MapPoint(bounds, rb.Center)); WriteAttr("RadiusX", Math.Abs(rb.RadiusX * bounds.Width)); WriteAttr("RadiusY", Math.Abs(rb.RadiusY * bounds.Height)); WriteAttr("GradientOrigin", Utility.MapPoint(bounds, rb.GradientOrigin)); WriteGradientStops("RadialGradientBrush", rb.GradientStops); break; } ImageBrush ib = brush as ImageBrush; if (ib != null) { WriteTileBrush("ImageBrush", ib, bounds); WriteBitmap("ImageSource", ib.ImageSource); WriteTransform("Transform", ib.Transform, ib.RelativeTransform, bounds); break; } DrawingBrush db = brush as DrawingBrush; if (db != null) { WriteTileBrush("VisualBrush", db, bounds); Matrix trans = Utility.MergeTransform(db.Transform, db.RelativeTransform, bounds); WriteTransform("Transform", trans); // Calculate approximate transformation from viewbox to world to serve as hint // for bitmap effect rasterization size. Matrix drawingToWorldTransform = Utility.CreateViewboxToViewportTransform(db, bounds); drawingToWorldTransform.Append(trans); _writer.WriteStartElement("VisualBrush.Visual"); WriteDrawingBody(db.Drawing, drawingToWorldTransform); _writer.WriteEndElement(); break; } VisualBrush vb = brush as VisualBrush; if (vb != null) { SaveResetState(); WriteTileBrush("VisualBrush", vb, bounds); WriteTransform("Transform", vb.Transform, vb.RelativeTransform, bounds); _writer.WriteStartElement("VisualBrush.Visual"); VisualTreeFlattener flattener = new VisualTreeFlattener(this, _pageSize); _writer.WriteStartElement("Canvas"); flattener.VisualWalk(vb.Visual); _writer.WriteEndElement(); _writer.WriteEndElement(); RestoreState(); break; } { Debug.Assert(false, "Brush not supported"); WriteBrushHeader(brush.GetType().ToString(), brush); } break; } _writer.WriteEndElement(); _writer.Flush(); _writer = oldwriter; return swriter.GetStringBuilder(); } #endregion #region Protected Fields protected System.Xml.XmlWriter _writer; protected System.Xml.XmlWriter _resWriter; protected System.Xml.XmlWriter _bodyWriter; protected int _brushId; // = 0; protected int _bitmapId; // = 0; protected PackageSerializationManager _manager; protected XpsTokenContext _context; protected Stack _tcoStack; // Transform, Clip, Opacity stack // resource dictionary objects protected ArrayList _objects; protected ArrayList _objnams; // common properties to apply to next element write protected double _opacity = 1.0; protected Brush _opacityMask; // = null; protected Transform _transform; // = null; protected Geometry _clip; // = null; protected string _coordFormat = "#0.##"; protected Matrix _worldTransform = Matrix.Identity; protected int _forceGeneral; //= 0; // preserve serialization attributes protected String _nameAttr; // = null; protected Visual _node; protected Uri _navigateUri; protected Size _pageSize; protected bool _exceedFloatLimit; protected bool _exceedPointLimit; protected int _totalElementCount; #endregion Protected Fields #region Private methods // Writes a brush even if transparent. // Transparent brushes are used with hyperlink Paths. protected void WriteBrush(string attribute, Brush brush, Rect bounds) { if (brush != null) { string str = SimpleBrushToString(brush); if (str != null) { _writer.WriteAttributeString(attribute, str); } else { string ob = FindBrush(brush, bounds); if ((_manager != null) || (_forceGeneral >= 1) ) // to container | within resource dictionary { _writer.WriteAttributeString(attribute, "{StaticResource " + ob + "}"); } // to loose files else { _writer.WriteAttributeString(attribute, "{DynamicResource " + ob + "}"); } } } } // Writes a pen even if transparent (but not if null). // Transparent pen still affects rendering, since it'll shrink the fill. protected void WritePen(Pen pen, Rect bounds, bool isLineGeometry) { if (pen != null && !PenProxy.IsNull(pen)) { WriteBrush("Stroke", pen.Brush, bounds); WriteAttr("StrokeThickness", Math.Abs(pen.Thickness)); WriteAttr("StrokeStartLineCap", pen.StartLineCap, PenLineCap.Flat); WriteAttr("StrokeEndLineCap", pen.EndLineCap, PenLineCap.Flat); if (!isLineGeometry) { // not a single line segment, properties affecting line joins are relevant if (pen.LineJoin == PenLineJoin.Miter) { WriteAttr("StrokeMiterLimit", Math.Max(1.0, pen.MiterLimit)); } else { WriteAttr("StrokeLineJoin", pen.LineJoin); } } if ((pen.DashStyle != null) && (pen.DashStyle.Dashes.Count != 0)) { WriteAttr("StrokeDashCap", pen.DashCap); WriteAttr("StrokeDashOffset", pen.DashStyle.Offset); // // If there are an odd number of elements in StrokeDashArray // duplicate the elements. This results in the repeating pattern // demonstrated by odd elements in pen. // DoubleCollection dashes = new DoubleCollection(); foreach( double d in pen.DashStyle.Dashes ) { dashes.Add(Math.Abs(d)); } if( pen.DashStyle.Dashes.Count%2 == 0 ) { WriteAttr("StrokeDashArray", dashes); } else { string doubleString = GetString(dashes)+" "+GetString(dashes); _writer.WriteAttributeString("StrokeDashArray",doubleString ); } } } } static private bool IsUniformScale(Matrix mat) { if (mat.IsIdentity) { return true; } else { return Utility.IsZero(mat.M11 - mat.M22) && Utility.IsZero(mat.M12) && Utility.IsZero(mat.M21); } } // convert Matrix to string private void AppendMatrix(StringBuilder rslt, Matrix mat) { if (!Utility.IsIdentity(mat)) { rslt.Append(CheckFloat(mat.M11).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); rslt.Append(CheckFloat(mat.M12).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); rslt.Append(CheckFloat(mat.M21).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); rslt.Append(CheckFloat(mat.M22).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); rslt.Append(CheckFloat(mat.OffsetX).ToString(CultureInfo.InvariantCulture)); rslt.Append(","); rslt.Append(CheckFloat(mat.OffsetY).ToString(CultureInfo.InvariantCulture)); } } private void WritePathFigureCollection(PathFigureCollection figures, bool forFill, bool forStroke) { int pc = 0; foreach (PathFigure p in figures) { // When filling only, skip not filled figures if (forFill && !forStroke && !p.IsFilled) { continue; } _writer.WriteStartElement("PathFigure"); pc ++; WriteAttr("StartPoint", p.StartPoint); WriteBool("IsClosed", p.IsClosed); if (!p.IsFilled) { WriteAttr("IsFilled", "false"); } PathSegmentCollection segments = p.Segments; int count = segments.Count; for (int i = 0; i < count; i ++) { PathSegment ps = segments[i]; // When stroking only, skip non-stroked segment if next segment is not either if (forStroke && !forFill && !ps.IsStroked) { // Non-stroked segments is still useful for providing starting point, // so they can't be totally skipped if ((i < count - 1) && !segments[i + 1].IsStroked) { continue; } } PolyLineSegment pl = ps as PolyLineSegment; if (pl != null) { _writer.WriteStartElement("PolyLineSegment"); WriteAttr("Points", pl.Points); pc += pl.Points.Count; } else if (ps is PolyBezierSegment) { PolyBezierSegment l = ps as PolyBezierSegment; _writer.WriteStartElement("PolyBezierSegment"); WriteAttr("Points", l.Points); pc += l.Points.Count; } else if (ps is LineSegment) { LineSegment l = ps as LineSegment; _writer.WriteStartElement("PolyLineSegment"); WriteAttr("Points", l.Point); pc ++; } else if (ps is BezierSegment) { BezierSegment b = ps as BezierSegment; _writer.WriteStartElement("PolyBezierSegment"); StringBuilder rslt = new StringBuilder(); AppendPoint(rslt, b.Point1, Matrix.Identity); rslt.Append(' '); AppendPoint(rslt, b.Point2, Matrix.Identity); rslt.Append(' '); AppendPoint(rslt, b.Point3, Matrix.Identity); _writer.WriteAttributeString("Points", rslt.ToString()); pc += 3; } else if (ps is ArcSegment) { ArcSegment a = ps as ArcSegment; if (a.Size.IsEmpty || a.Size.Width == 0 || a.Size.Height == 0) { // empty size results in line segment _writer.WriteStartElement("PolyLineSegment"); WriteAttr("Points", a.Point); pc ++; } else { _writer.WriteStartElement("ArcSegment"); WriteAttr("Point", a.Point); WriteAttr("Size", a.Size); WriteAttr("RotationAngle", a.RotationAngle); WriteBool("IsLargeArc", a.IsLargeArc); WriteAttr("SweepDirection", a.SweepDirection); pc += 2; } } else if (ps is QuadraticBezierSegment) { QuadraticBezierSegment b = ps as QuadraticBezierSegment; _writer.WriteStartElement("PolyQuadraticBezierSegment"); StringBuilder rslt = new StringBuilder(); AppendPoint(rslt, b.Point1, Matrix.Identity); rslt.Append(' '); AppendPoint(rslt, b.Point2, Matrix.Identity); _writer.WriteAttributeString("Points", rslt.ToString()); pc += 2; } else if (ps is PolyQuadraticBezierSegment) { PolyQuadraticBezierSegment b = ps as PolyQuadraticBezierSegment; _writer.WriteStartElement("PolyQuadraticBezierSegment"); WriteAttr("Points", b.Points); pc += b.Points.Count; } else { _writer.WriteStartElement(ps.ToString() + "PathSegment not handled"); } if (!ps.IsStroked) { WriteAttr("IsStroked", "false"); } _writer.WriteEndElement(); } _writer.WriteEndElement(); } if (pc > MaxPointCount) { _exceedPointLimit = true; } } // Check if brush and/or pen actually paint anything visible static private bool Visible(Brush brush, Pen pen) { if (brush != null) { if (!BrushProxy.IsEmpty(brush)) { return true; } } if (pen != null) { if ((pen.Brush != null) && !BrushProxy.IsEmpty(pen.Brush)) { return true; } } return false; } static private char Ord(bool b) { if (b) { return '1'; } else { return '0'; } } static private char Ord(SweepDirection d) { if (d == SweepDirection.Clockwise) { return '1'; } else { return '0'; } } // Convert a simple PathGeometry to a string which can be inlined. // Return null if it does not fit short-hand syntax private string PathGeometryToString(PathGeometry path, Matrix map, bool forFill, bool forStroke) { if ((path.Transform != null) && !Utility.IsIdentity(path.Transform)) { map = path.Transform.Value * map; } PathFigureCollection figures = path.Figures; StringBuilder rslt = new StringBuilder(); int pc = 0; foreach (PathFigure p in figures) { if (!p.IsFilled) { // Mini-language does not support IsFilled=false if (forStroke && forFill) { return null; } // When filling only, skip not filled PathFigure if (forFill) { continue; } // When stroking only, ignore IsFilled flag } PathSegmentCollection segments = p.Segments; // Start point if (rslt.Length == 0) { if (path.FillRule == FillRule.EvenOdd) { // EvenOdd is the default, don't write anything } else { rslt.Append("F1"); } } rslt.Append('M'); AppendPoint(rslt, p.StartPoint, map); pc ++; // Segments foreach (PathSegment ps in segments) { if (forStroke) { if (!ps.IsStroked || ps.IsSmoothJoin) { // Mini-language does not support IsStroked=false, IsSmoothJoin=false return null; } } if (ps is PolyLineSegment) { PolyLineSegment l = ps as PolyLineSegment; rslt.Append('L'); pc += AppendPoints(rslt, l.Points, map); } else if (ps is PolyBezierSegment) { PolyBezierSegment l = ps as PolyBezierSegment; rslt.Append('C'); pc += AppendPoints(rslt, l.Points, map); } else if (ps is LineSegment) { LineSegment l = ps as LineSegment; rslt.Append('L'); AppendPoint(rslt, l.Point, map); pc ++; } else if (ps is BezierSegment) { BezierSegment b = ps as BezierSegment; rslt.Append('C'); AppendPoint(rslt, b.Point1, map); rslt.Append(' '); AppendPoint(rslt, b.Point2, map); rslt.Append(' '); AppendPoint(rslt, b.Point3, map); pc += 3; } else if (ps is ArcSegment) { if (IsUniformScale(map)) { ArcSegment a = ps as ArcSegment; Size s = a.Size; if (s.IsEmpty || s.Width == 0 || s.Height == 0) { // empty size results in line segment rslt.Append('L'); AppendPoint(rslt, a.Point, map); pc ++; } else { rslt.Append('A'); AppendPoint(rslt, new Point(s.Width * map.M11, s.Height * map.M22), Matrix.Identity); rslt.Append(' '); rslt.Append(a.RotationAngle); rslt.Append(' '); rslt.Append(Ord(a.IsLargeArc)); rslt.Append(' '); rslt.Append(Ord(a.SweepDirection)); rslt.Append(' '); AppendPoint(rslt, a.Point, map); pc += 2; } } else { // ArcSegment can only be transformed inline by uniform scaling + translation return null; } } else if (ps is QuadraticBezierSegment) { QuadraticBezierSegment b = ps as QuadraticBezierSegment; rslt.Append('Q'); AppendPoint(rslt, b.Point1, map); rslt.Append(' '); AppendPoint(rslt, b.Point2, map); pc += 2; } else if (ps is PolyQuadraticBezierSegment) { PolyQuadraticBezierSegment l = ps as PolyQuadraticBezierSegment; rslt.Append('Q'); pc += AppendPoints(rslt, l.Points, map); } else { return null; } } // Closed? if (p.IsClosed) { rslt.Append('Z'); } } if (pc > MaxPointCount) { _exceedPointLimit = true; } return rslt.ToString(); } private void WriteTransform(string attribute, Transform trans, Transform relative, Rect bounds) { Matrix mat = Utility.MergeTransform(trans, relative, bounds); if (!Utility.IsIdentity(mat)) { _writer.WriteAttributeString(attribute, GetString(mat)); } } private void WriteTransform(string attribute, Matrix trans) { if (!Utility.IsIdentity(trans)) { _writer.WriteAttributeString(attribute, GetString(trans)); } } private static Transform Append(Transform trans, Matrix mat) { Matrix m = Matrix.Identity; if (trans != null) { m = trans.Value; } m.Append(mat); return new MatrixTransform(m); } private void WriteBool(string attr, bool val) { string str; if (val) { str = "true"; } else { str = "false"; } _writer.WriteAttributeString(attr, str); } private void WriteFillRule(FillRule rule) { string str = null; if (rule == FillRule.Nonzero) { str = "NonZero"; } else { // EvenOdd is the default, don't write anything } if (str != null) { _writer.WriteAttributeString("FillRule", str); } } internal bool WriteGeometry(string element, string attribute, Geometry geo, Matrix map, bool asElement, bool forFill, bool forStroke) { Debug.Assert(forFill || forStroke, "Either forFill or forStoke should be true"); PathGeometry pg = null; string p = null; pg = Utility.GetAsPathGeometry(geo); if (IsPathGeometryEmpty(pg, forFill, forStroke)) { return false; } if (attribute != null) { if (!asElement && (pg != null)) { p = PathGeometryToString(pg, map, forFill, forStroke); // If it can be converted to a string, output as attribute if (p != null) { _writer.WriteAttributeString(attribute, p); return false; } } // Output as element _writer.WriteStartElement(element + "." + attribute); } _writer.WriteStartElement("PathGeometry"); Transform trans = Append(pg.Transform, map); WriteFillRule(pg.FillRule); p = PathGeometryToString(pg, trans.Value, forFill, forStroke); if (p != null) { // Remove "Fn " prefix _writer.WriteAttributeString("Figures", p.Substring(3)); } else { PushCoordinateScope(trans); WriteTransform("Transform", trans, null, Rect.Empty); WritePathFigureCollection(pg.Figures, forFill, forStroke); PopCoordinateScope(); } _writer.WriteEndElement(); if (attribute != null) { _writer.WriteEndElement(); } return true; } private static bool IsPathFigureEmpty(PathFigureCollection figures, bool forFill, bool forStroke) { if (figures == null) { return true; } foreach (PathFigure p in figures) { // When filling only, skip not filled PathFigure if (forFill && !forStroke && !p.IsFilled) { continue; } PathSegmentCollection segments = p.Segments; if (segments != null) { foreach (PathSegment ps in segments) { // When stroking only, skip not stroked PathSegment if (forStroke && !forFill && !ps.IsStroked) { continue; } // Found something real return false; } } } return true; } /// /// Check geometry for emptyness before generating it. Empty geometry not allowed in Metro /// /// /// /// ///private static bool IsPathGeometryEmpty(PathGeometry pg, bool forFill, bool forStroke) { if ((pg == null) || pg.Bounds.IsEmpty) { return true; } if (!forStroke && !Utility.IsRenderVisible(pg.Bounds)) { // When not stroking, geometry with zero-area bounds displays nothing, otherwise // the stroke may widen geometry to display something. return true; } return IsPathFigureEmpty(pg.Figures, forFill, forStroke); } /// /// Returns true if input is valid in an XML file, according to specification at /// http://www.w3.org/TR/2004/REC-xml-20040204/#charsets. /// /// input character private static bool IsXmlValidChar(char c) { if ( c == 0x09 || c == 0x0A || c == 0x0D || (c >= 0x20 && c <= 0xD7FF) || (c >= 0xE000 && c <= 0xFFFD) ) { // valid return true; } else { return false; } } ////// Filters invalid XML characters from GlyphRun by replacing with spaces. /// /// ///Returns the filtered GlyphRun if modified, otherwise the original GlyphRun private static GlyphRun FilterXmlInvalidChar(GlyphRun glyphRun) { if (glyphRun.Characters == null) { // no characters to worry about return glyphRun; } // // Fix bug 1334838: XPS Serialization: Incorrect XML characters not filtered out of S0 markup // // We temporarily fix by replacing invalid characters with spaces. The advance widths aren't // touched, and so visual appearance should remain the same except for missing glyphs // corresponding to replaced characters. // ListfilteredCharacters = null; for (int i = 0; i < glyphRun.Characters.Count; i++) { if (!IsXmlValidChar(glyphRun.Characters[i])) { if (filteredCharacters == null) { filteredCharacters = new List (glyphRun.Characters); } // replace invalid character with space filteredCharacters[i] = ' '; } } if (filteredCharacters == null) { // no filtering needed return glyphRun; } else { // return filtered GlyphRun GlyphRun filtered = new GlyphRun( glyphRun.GlyphTypeface, glyphRun.BidiLevel, glyphRun.IsSideways, glyphRun.FontRenderingEmSize, glyphRun.GlyphIndices, glyphRun.BaselineOrigin, glyphRun.AdvanceWidths, glyphRun.GlyphOffsets, filteredCharacters, glyphRun.DeviceFontName, glyphRun.ClusterMap, glyphRun.CaretStops, glyphRun.Language ); return filtered; } } /// /// Gets URI as string, which may be relative if pointing within same XPS document. /// /// ///private string GetUriAsString(Uri uri) { return uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.SafeUnescaped); } /// /// Determines if common attributes need to be preserved even if the element is transparent. /// ////// Attributes that need to be preserved even if the element is transparent: /// - Name: May be used as link target. /// - NavigateUri: Hyperlink clickable area. /// private bool PreserveTransparent() { return _nameAttr != null || _navigateUri != null; } ////// Writes attributes common to Canvas/Path/Glyphs. /// /// Also write automation properties. Glyphs does not have automation properties. private void WriteCommonAttrs(bool bWriteAutomation) { if (_nameAttr != null) { Debug.Assert(_nameAttr.Length > 0, "Empty _nameAttr"); WriteAttr("Name", _nameAttr); _nameAttr = null; } if (_node != null) { if( bWriteAutomation ) { string apName = AutomationProperties.GetName(_node); if (! String.IsNullOrEmpty(apName)) { WriteAttr("AutomationProperties.Name", apName); } string apHelpText = AutomationProperties.GetHelpText(_node); if (! String.IsNullOrEmpty(apHelpText)) { WriteAttr("AutomationProperties.HelpText", apHelpText); } } _node = null; } if (_navigateUri != null) { WriteAttr("FixedPage.NavigateUri", _navigateUri); _navigateUri = null; } } #endregion #region IMetroDrawingContext virtual methods override // Check for relative brush static public bool NeedBounds(Brush b) { if (b == null) { return false; } TileBrush tb = b as TileBrush; if (tb != null) { return (tb is VisualBrush) || (tb.ViewportUnits == BrushMappingMode.RelativeToBoundingBox); } GradientBrush gb = b as GradientBrush; if (gb != null) { return gb.MappingMode == BrushMappingMode.RelativeToBoundingBox; } return false; } #if INVERSE_PATH_FOR_BRUSH_SHARING // Get inverse transfrom from bounding rectangle, adjust RenderTransform accordingly // so that brushes can be independent of bounding boxes Matrix GetBoundsInverse(Rect bounds) { Matrix mat = Matrix.Identity; mat.Scale(bounds.Width, bounds.Height); mat.Translate(bounds.Left, bounds.Top); if (_transform != null) { mat.Append(_transform.Value); } _transform = new MatrixTransform(mat); Matrix inverse = Matrix.Identity; inverse.Translate(-bounds.Left, -bounds.Top); inverse.Scale(1 / bounds.Width, 1 / bounds.Height); return inverse; } #endif #if SPLITTING_PATH ////// Check if a geometry is different under filling and stroking, check for IsFilled=false and IsStroked=false /// /// ///internal static bool IsGeometryPolymophic(Geometry geo) { PathGeometry pg = geo as PathGeometry; if (pg != null) { PathFigureCollection figures = pg.Figures; if (figures != null) { foreach (PathFigure p in figures) { if (!p.IsFilled) { return true; } PathSegmentCollection segments = p.Segments; if (segments != null) { foreach (PathSegment ps in segments) { if (!ps.IsStroked) { return true; } } } } } return false; } CombinedGeometry cg = geo as CombinedGeometry; if (cg != null) { return IsGeometryPolymophic(cg.Geometry1) || IsGeometryPolymophic(cg.Geometry2); } GeometryGroup gg = geo as GeometryGroup; if (gg != null) { if (gg.Children != null) { foreach (Geometry g in gg.Children) { if (IsGeometryPolymophic(g)) { return true; } } } } return false; } #endif /// /// Draw a Geometry with the provided Brush and/or Pen. /// If both the Brush and Pen are null this call is a no-op. /// void IMetroDrawingContext.DrawGeometry(Brush brush, Pen pen, Geometry geometry) { if (geometry == null) { return; } bool forFill = Visible(brush, null); bool forStroke = Visible(null, pen); if (PreserveTransparent()) { // // Preserve Path element even if transparent brush/pen, since attributes // attached to this Path need preservation, i.e. hyperlink. // // If null fill, change to transparent fill to ensure hyperlink is clickable. // if (brush == null) { forFill = true; // have WriteGeometry treat as fill even though it isn't visible brush = Brushes.Transparent; } } else { if (!Visible(brush, pen)) return; } #if SPLITTING_PATH if ((brush != null) && (pen != null) && IsGeometryPolymophic(geometry)) { ((IMetroDrawingContext) this).DrawGeometry(brush, null, geometry); brush = null; } #endif Rect bounds = geometry.GetRenderBounds(pen); if (!Utility.IsRenderVisible(bounds)) { return; } _tcoStack.Push(_transform); Matrix inverse = Matrix.Identity; #if INVERSE_PATH_FOR_BRUSH_SHARING // To generate more sharable relative brushes, it's possible to use a unit bounding box // and apply a reverse transformation to geometry. // This can generate strange viewbox and complicated path. // Disable for the memoent if ((pen == null) && (_clip == null) && (_opacityMask == null) && !(geometry is CombinedGeometry) && NeedBounds(brush)) { inverse = GetBoundsInverse(bounds); bounds = UnitRect; } #endif _writer.WriteStartElement("Path"); WriteCommonAttrs(true); bool isLineGeometry = Utility.IsLineSegment(geometry); WritePen(pen, bounds, isLineGeometry); WriteBrush("Fill", brush, geometry.Bounds); bool asElement = WriteTCO("Path", _transform, _clip, Matrix.Identity, _opacity, _opacityMask, bounds); WriteGeometry("Path", "Data", geometry, inverse, asElement, forFill, forStroke); _writer.WriteEndElement(); _transform = _tcoStack.Pop() as Transform; _totalElementCount ++; ReportLimitViolation(); } ////// Draw an Image into the region specified by the Rect, which may be animate. /// The Image will potentially be stretched and distorted to fit the Rect. /// For more fine grained control, consider filling a Rect with an ImageBrush via DrawRect. /// void IMetroDrawingContext.DrawImage(ImageSource image, Rect rectangle) { if (image != null) { Brush brush = new ImageBrush((BitmapSource)image); ((IMetroDrawingContext)this).DrawGeometry(brush, null, new RectangleGeometry(rectangle)); } else if (PreserveTransparent()) { // Transparent image, but element has attributes that need preservation (i.e. Name or NavigateUri). ((IMetroDrawingContext)this).DrawGeometry(null, null, new RectangleGeometry(rectangle)); } } ////// For translation only transform, extract out translation /// static private Transform ExtractTranslation(Transform trans, out double dx, out double dy) { dx = 0; dy = 0; if (!Utility.IsIdentity(trans)) { Matrix mat = trans.Value; if (Utility.IsOne(mat.M11) && Utility.IsOne(mat.M22) && Utility.IsZero(mat.M12) && Utility.IsZero(mat.M21)) { dx = mat.OffsetX; dy = mat.OffsetY; return Transform.Identity; } } return trans; } ////// Draw a GlyphRunAsImage. /// ////// This is used if GlyphRun cannot be embedded in XPS; therefore rasterization is needed. /// internal void DrawGlyphRunAsImage(Brush foreground, GlyphRun glyphRun) { Toolbox.StartEvent(Toolbox.DRXRASTERGUID); // rasterize GlyphRunDrawing to bitmap GlyphRunDrawing drawing = new GlyphRunDrawing(foreground, glyphRun); Matrix bitmapToDrawingTransform; BitmapSource bitmap = Utility.RasterizeDrawing( drawing, drawing.Bounds, _worldTransform, out bitmapToDrawingTransform ); if (bitmap != null) { // // Draw the rasterized glyphs. bitmapToDrawingTransform should strictly // be a translation/scaling transform, which is why we can use Rect.Transform and // avoid a Push/Pop. // Debug.Assert(Utility.IsScaleTranslate(bitmapToDrawingTransform)); Rect rect = new Rect(0, 0, bitmap.Width, bitmap.Height); rect.Transform(bitmapToDrawingTransform); ((IMetroDrawingContext)this).DrawImage(bitmap, rect); } Toolbox.EndEvent(Toolbox.DRXRASTERGUID); } ////// Critical - It calls critical internal function CriticalFileReadPermission /// [SecurityCritical] private static bool EmbeddingAllowed(GlyphTypeface typeface) { CodeAccessPermission fontReadPermission = typeface.CriticalFileReadPermission; FontEmbeddingRight embeddingRights = FontEmbeddingRight.Installable; if (fontReadPermission != null) { fontReadPermission.Assert(); // Blessed assert } try { embeddingRights = typeface.EmbeddingRights; } finally { if (fontReadPermission != null) { CodeAccessPermission.RevertAssert(); } } return (XpsFontSubsetter.DetermineEmbeddingAction(embeddingRights) != FontEmbeddingAction.ImageOnlyFont); } ////// Draw a GlyphRun. /// ////// Critical - It uses internal permission to get FontUri /// TreatAsSafe - The Uri is used for font subseeting, and font is written to container for controoled printing /// [SecurityCritical, SecurityTreatAsSafe] void IMetroDrawingContext.DrawGlyphRun(Brush foreground, GlyphRun glyphRun) { if (glyphRun == null) { return; } if (PreserveTransparent()) { if (foreground == null) { // Give transparent foreground to ensure hyperlink is clickable. foreground = Brushes.Transparent; } } else { if (!Visible(foreground, null)) return; } if (!EmbeddingAllowed(glyphRun.GlyphTypeface)) { this.DrawGlyphRunAsImage(foreground, glyphRun); return; } _writer.WriteStartElement("Glyphs"); double dx = 0, dy = 0; Transform trans = _transform; Matrix clipMat = Matrix.Identity; // Optimization: apply translation to OriginX/OriginY if possible if ((foreground is SolidColorBrush) && (_opacityMask == null)) { trans = ExtractTranslation(_transform, out dx, out dy); if (_clip != null) { clipMat = new Matrix(1, 0, 0, 1, dx, dy); } } WriteCommonAttrs(false); WriteAttr("OriginX", glyphRun.BaselineOrigin.X + dx); WriteAttr("OriginY", glyphRun.BaselineOrigin.Y + dy); WriteAttr("FontRenderingEmSize", glyphRun.FontRenderingEmSize); Uri uri = Utility.GetFontUri(glyphRun.GlyphTypeface); if (_manager != null) { TypeConverter converter = _manager.GetTypeConverter(typeof(GlyphRun)); if (converter != null) { uri = converter.ConvertTo(_context, null, glyphRun, typeof(Uri)) as Uri; } } WriteAttr("FontUri", uri); WriteAttr("StyleSimulations", glyphRun.GlyphTypeface.StyleSimulations, StyleSimulations.None); // BidiLevel must be in range [0, 61] int bidiLevel = glyphRun.BidiLevel; if (bidiLevel > 61) { bidiLevel = 61; } else if (bidiLevel < 0) { bidiLevel = 0; } WriteAttr("BidiLevel", bidiLevel, 0); if (glyphRun.IsSideways) { WriteBool("IsSideways", glyphRun.IsSideways); } // // Fix bug 1334838: XPS Serialization: Incorrect XML characters not filtered out of S0 markup // // Filter XML-invalid characters from GlyphRun, returning the filtered GlyphRun. The filtering // affects only UnicodeString. // GlyphRun serializeGlyphRun = FilterXmlInvalidChar(glyphRun); // serialize complex properties, running full markup size optimizations // per [....]/text/DesignDocsAndSpecs/Glyphs%20element%20and%20GlyphRun%20object.htm#optimizing GlyphsSerializer glyphsSerializer = new GlyphsSerializer(serializeGlyphRun); string characters, indices, caretStops; glyphsSerializer.ComputeContentStrings(out characters, out indices, out caretStops); bool exceedGlyphCount = false; if (!String.IsNullOrEmpty(characters)) { // Leading '{' in a string is used for markup extension. // Prefix it with '{}' to avoid being confused with markup extension if (characters[0] == '{') { characters = "{}" + characters; } if (serializeGlyphRun.Characters.Count > MaxGlyphCount) { exceedGlyphCount = true; } WriteAttr("UnicodeString", characters); } if (!String.IsNullOrEmpty(indices)) { if (serializeGlyphRun.GlyphIndices.Count > MaxGlyphCount) { exceedGlyphCount = true; } WriteAttr("Indices", indices); } if (!String.IsNullOrEmpty(caretStops)) { WriteAttr("CaretStops", caretStops); } Rect bounds = UnitRect; if (NeedBounds(_opacityMask) || NeedBounds(foreground)) { bounds = glyphRun.ComputeInkBoundingBox(); bounds.X += glyphRun.BaselineOrigin.X + dx; bounds.Y += glyphRun.BaselineOrigin.Y + dy; } WriteBrush("Fill", foreground, bounds); if (glyphRun.Language != null && glyphRun.Language != _manager.Language) { // Only write language attribute if it doesn't match the fixedpage language. // WriteTCO might generate an element, so must write this attribute before. WriteAttr(XpsS0Markup.XmlLang, glyphRun.Language.ToString()); } WriteTCO("Glyphs", trans, _clip, clipMat, _opacity, _opacityMask, bounds); _writer.WriteEndElement(); if (exceedGlyphCount) { _writer.WriteComment("XPSLimit:GlyphCount"); } _totalElementCount ++; ReportLimitViolation(); } ////// Pop the most recent Push operation, which may have been a Clip, Opacity, Transform, etc. /// void IMetroDrawingContext.Pop() { _transform = null; _clip = null; _opacity = 1.0; _opacityMask = null; int level = (int)_tcoStack.Pop(); PopCoordinateScope(); while (level > 0) { _writer.WriteEndElement(); level --; } if (_tcoStack.Count == 0) // end of page { if (_brushId > MaxResourceCount) { _writer.WriteComment("XPSLimit:ResourceCount"); } if (_totalElementCount > MaxElementCount) { _writer.WriteComment("XPSLimit:ElementCount"); } } } void IMetroDrawingContext.Comment(String str) { _writer.WriteComment(str); } bool WriteTCO(string element, Transform transform, Geometry clip, Matrix clipMat, double opacity, Brush opacityMask, Rect bounds) { // Extract opacity from SolidColorBrush OpacityMask if (opacityMask != null) { SolidColorBrush sb = opacityMask as SolidColorBrush; if (sb != null) { opacity *= Utility.NormalizeOpacity(sb.Color.ScA) * Utility.NormalizeOpacity(opacityMask.Opacity); opacityMask = null; } } if (!Utility.IsOne(opacity)) { WriteAttr("Opacity", Math.Min(Math.Max( opacity, 0.0),1.0)); } if (opacityMask != null && !BrushProxy.IsEmpty(opacityMask)) { WriteBrush("OpacityMask", opacityMask, bounds); } WriteTransform("RenderTransform", transform, null, Rect.Empty); bool asElement = false; if (clip != null) { asElement = WriteGeometry(element, "Clip", clip, clipMat, false, true, false); } return asElement; } void SetCoordinateFormat(double scale) { scale = scale * PrecisionDPI / 9600; if (scale > 1000) { _coordFormat = "G"; // 15 digits } else if (scale > 100) { _coordFormat = "#0.#####"; // 5 digits } else if (scale > 10) { _coordFormat = "#0.####"; // 4 digits } else if (scale > 1) { _coordFormat = "#0.###"; // 3 digits } else if (scale > 0.1) { _coordFormat = "#0.##"; // 2 digits } else { _coordFormat = "#0.#"; // 1 digit } } void PushCoordinateScope(Transform transform) { _tcoStack.Push(_worldTransform); _tcoStack.Push(_coordFormat); if (!Utility.IsIdentity(transform)) { Matrix mat = transform.Value; mat.Append(_worldTransform); _worldTransform = mat; SetCoordinateFormat(Math.Min(Utility.GetScaleX(mat), Utility.GetScaleY(mat))); } } void PopCoordinateScope() { _coordFormat = _tcoStack.Pop() as string; _worldTransform = (Matrix)_tcoStack.Pop(); } void IMetroDrawingContext.Push( Matrix mat, Geometry clip, double opacity, Brush opacityMask, Rect maskBounds, bool onePrimitive, // serialization attributes String nameAttr, Visual node, Uri navigateUri, EdgeMode edgeMode ) { Debug.Assert(nameAttr == null || nameAttr.Length > 0, "Bad name attribute"); Transform transform = new MatrixTransform(mat); PushCoordinateScope(transform); bool noTrans = Utility.IsIdentity(transform); if (node != null) { _node = node; } int elementLevels = 0; if ((clip == null) && noTrans && Utility.IsOne(opacity) && (opacityMask == null) && nameAttr == null && navigateUri == null && edgeMode == EdgeMode.Unspecified) { // If there is no clip, transform and opacity, nothing to generate } else if (onePrimitive && edgeMode == EdgeMode.Unspecified) // EdgeMode only valid on Canvas { _transform = transform; _clip = clip; _opacity = opacity; _opacityMask = opacityMask; if (nameAttr != null) { Debug.Assert(_nameAttr == null, "Empty"); _nameAttr = nameAttr; } if (navigateUri != null) { _navigateUri = navigateUri; } } else { _writer.WriteStartElement("Canvas"); // write common attributes _nameAttr = nameAttr; _navigateUri = navigateUri; WriteCommonAttrs(true); // write RenderOptions.EdgeMode if (edgeMode != EdgeMode.Unspecified) { WriteAttr("RenderOptions.EdgeMode", edgeMode); } // write transform, clip, opacity WriteTCO("Canvas", transform, clip, Matrix.Identity, opacity, opacityMask, maskBounds); elementLevels = 1; } // push number of element levels introduced by this IMetroDrawingContext.Push _tcoStack.Push(elementLevels); } #endregion Public Methods } #endregion } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FontWeight.cs
- MexBindingBindingCollectionElement.cs
- SystemPens.cs
- HostedHttpRequestAsyncResult.cs
- FragmentQueryKB.cs
- OpenTypeLayout.cs
- SaveFileDialog.cs
- XmlEncoding.cs
- ParallelQuery.cs
- ThemeDirectoryCompiler.cs
- MethodAccessException.cs
- SqlFacetAttribute.cs
- TextEncodedRawTextWriter.cs
- IdentityValidationException.cs
- EntityStoreSchemaFilterEntry.cs
- mansign.cs
- TextBoxRenderer.cs
- InfoCardRSACryptoProvider.cs
- ACE.cs
- LayoutManager.cs
- RenderingBiasValidation.cs
- PreviewPageInfo.cs
- MexNamedPipeBindingElement.cs
- SqlParameterizer.cs
- PropertyOverridesTypeEditor.cs
- _DomainName.cs
- Misc.cs
- __Filters.cs
- PropertyDescriptorComparer.cs
- MergePropertyDescriptor.cs
- DataGridViewTextBoxColumn.cs
- TextRangeSerialization.cs
- ToolStripButton.cs
- SoapRpcMethodAttribute.cs
- Substitution.cs
- Statements.cs
- RemoteWebConfigurationHostServer.cs
- _ListenerResponseStream.cs
- ArgumentException.cs
- WebPartVerbsEventArgs.cs
- IndentedWriter.cs
- PointIndependentAnimationStorage.cs
- TextRangeAdaptor.cs
- IdentifierService.cs
- GlyphRun.cs
- ValueSerializer.cs
- DigitalSignature.cs
- DebugView.cs
- BitmapEffectGeneralTransform.cs
- PersonalizationProvider.cs
- Identity.cs
- ApplicationBuildProvider.cs
- MulticastDelegate.cs
- ExclusiveNamedPipeTransportManager.cs
- RegistrySecurity.cs
- WindowInteractionStateTracker.cs
- WebSysDescriptionAttribute.cs
- ElementHostAutomationPeer.cs
- TextServicesCompartment.cs
- FieldNameLookup.cs
- RemoteCryptoDecryptRequest.cs
- OneOf.cs
- Preprocessor.cs
- XhtmlCssHandler.cs
- Transform3D.cs
- ContourSegment.cs
- XmlChoiceIdentifierAttribute.cs
- CompiledXpathExpr.cs
- ExtendedProperty.cs
- Column.cs
- FileInfo.cs
- BamlLocalizationDictionary.cs
- BitmapEffectState.cs
- IisTraceWebEventProvider.cs
- InvokeMethodDesigner.xaml.cs
- GlyphInfoList.cs
- PolicyLevel.cs
- FreezableOperations.cs
- DoubleAnimationClockResource.cs
- ScriptControl.cs
- WebProxyScriptElement.cs
- OptimizerPatterns.cs
- SHA256.cs
- AttachedPropertyBrowsableAttribute.cs
- UdpConstants.cs
- HtmlControlAdapter.cs
- HuffModule.cs
- _Rfc2616CacheValidators.cs
- MaskInputRejectedEventArgs.cs
- PackagingUtilities.cs
- State.cs
- DoubleUtil.cs
- ReceiveMessageContent.cs
- WinFormsUtils.cs
- PageThemeParser.cs
- DaylightTime.cs
- TransactionScopeDesigner.cs
- Rules.cs
- Size3DValueSerializer.cs
- LinqToSqlWrapper.cs