#region PDFsharp - A .NET library for processing PDF // // Authors: // Stefan Lange // // Copyright (c) 2005-2017 empira Software GmbH, Cologne Area (Germany) // // http://www.pdfsharp.com // http://sourceforge.net/projects/pdfsharp // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #endregion #if SILVERLIGHT using System.Collections.Generic; using System.Diagnostics; using System.Windows.Controls; using System.Windows.Shapes; using PdfSharp.Drawing; using PdfSharp.Internal; // ReSharper disable ConvertPropertyToExpressionBody namespace System.Windows.Media { /// /// The WPF graphic system has the DrawingContext class that implements the low-level /// primitives for retained drawing. The lowest level in Silverlight a some non-aggregated /// UI elements like several Shape objects, TextBlock and Glyphs. /// This Silverlight version of DrawingContext simplyfies the implementation of XGraphics. /// It converts function calls to DrawingContext into UI elements layered on Canvas /// objects. /// internal class AgDrawingContext { /// /// Initializes a new instance of the class. /// Drawing creates objects that are placed on the specified canvas. /// internal AgDrawingContext(Canvas canvas) { if (canvas == null) throw new ArgumentNullException("canvas"); // The size of the canvas is not used and does not matter. // The properties of the canvas are not modified. Instead a new // canvas is added to its Children list. _canvasStack.Push(canvas); PushCanvas(); } public void Close() { // There is nothing to close in Silverlight. } // There are no drawing objects in Silverlight. //public void DrawDrawing(Drawing drawing); public void DrawEllipse(Brush brush, Pen pen, Point center, double radiusX, double radiusY) { Ellipse ellipse = new Ellipse(); SetupShape(ellipse, center.X - radiusX, center.Y - radiusY, radiusX * 2, radiusY * 2, brush, pen); ellipse.Fill = brush; ActiveCanvas.Children.Add(ellipse); } //public void DrawEllipse(Brush brush, Pen pen, Point center, AnimationClock centerAnimations, double radiusX, AnimationClock radiusXAnimations, double radiusY, AnimationClock radiusYAnimations); public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) { Path path = new Path(); SetupShape(path, 0, 0, Double.NaN, Double.NaN, brush, pen); path.Data = geometry; ActiveCanvas.Children.Add(path); } public void DrawPath(Brush brush, Pen pen, Path path) { SetupShape(path, brush, pen); ActiveCanvas.Children.Add(path); } //public void DrawGlyphRun(Brush foregroundBrush, GlyphRun glyphRun); public void DrawImage(ImageSource imageSource, Rect rectangle) { // TODO Image image = new Image(); image.Source = imageSource; Canvas.SetLeft(image, rectangle.X); Canvas.SetTop(image, rectangle.Y); image.Width = rectangle.Width; image.Height = rectangle.Height; ActiveCanvas.Children.Add(image); } public void DrawLine(Pen pen, Point point0, Point point1) { #if true Line line = new Line(); SetupShape(line, 0, 0, Double.NaN, Double.NaN, null, pen); line.X1 = point0.X; line.Y1 = point0.Y; line.X2 = point1.X; line.Y2 = point1.Y; ActiveCanvas.Children.Add(line); #else Line line = new Line(); double x = Math.Min(point0.X, point1.X); double y = Math.Min(point0.Y, point1.Y); // Prevent clipping thick lines parallel to shape boundaries. double delta = 0; // 2 * pen.Thickness; //SetupShape(line, x - delta, y - delta, width + 2 * delta, height + 2 * delta, null, pen); SetupShape(line, x - delta, y - delta, Double.NaN, Double.NaN, null, pen); line.X1 = point0.X - x + delta; line.Y1 = point0.Y - y + delta; line.X2 = point1.X - x + delta; line.Y2 = point1.Y - y + delta; ActiveCanvas.Children.Add(line); #endif } public void DrawRectangle(Brush brush, Pen pen, Rect rect) { Rectangle rectangle = new Rectangle(); SetupShape(rectangle, rect.X, rect.Y, rect.Width, rect.Height, brush, pen); ActiveCanvas.Children.Add(rectangle); } public void DrawRoundedRectangle(Brush brush, Pen pen, Rect rect, double radiusX, double radiusY) { Rectangle rectangle = new Rectangle(); SetupShape(rectangle, rect.X, rect.Y, rect.Width, rect.Height, brush, pen); rectangle.RadiusX = radiusX; rectangle.RadiusY = radiusY; ActiveCanvas.Children.Add(rectangle); } static void SetupShape(Shape shape, double x, double y, double width, double height, Brush brush, Pen pen) { if (width < 0) // nan < 0 is false { x += width; width = -width; } if (height < 0) { y += height; height = -height; } Canvas.SetLeft(shape, x); Canvas.SetTop(shape, y); // Setting a double dependency property to Double.NaN is not the same // as simply not setting it. I consider this is a bug in the Silverlight run-time. if (!DoubleUtil.IsNaN(width)) shape.Width = width; if (!DoubleUtil.IsNaN(height)) shape.Height = height; SetupShape(shape, brush, pen); } static void SetupShape(Shape shape, Brush brush, Pen pen) { shape.Fill = brush; if (pen != null) { DoubleCollection dashArray = new DoubleCollection(); foreach (double value in pen.DashArray) dashArray.Add(value); shape.Stroke = pen.Brush; shape.StrokeThickness = pen.Thickness; shape.StrokeDashArray = dashArray; shape.StrokeDashOffset = pen.DashOffset; shape.StrokeStartLineCap = pen.StartLineCap; shape.StrokeEndLineCap = pen.EndLineCap; shape.StrokeDashCap = pen.DashCap; shape.StrokeLineJoin = pen.LineJoin; shape.StrokeMiterLimit = pen.MiterLimit; } } //public void DrawRoundedRectangle(Brush brush, Pen pen, Rect rectangle, AnimationClock rectangleAnimations, double radiusX, AnimationClock radiusXAnimations, double radiusY, AnimationClock radiusYAnimations); //public void DrawText(FormattedText formattedText, Point origin); //public void DrawVideo(MediaPlayer player, Rect rectangle); //public void DrawVideo(MediaPlayer player, Rect rectangle, AnimationClock rectangleAnimations); public void Pop(int count) { Debug.Assert(_canvasStack.Count - 1 > count); for (int idx = 0; idx < count; idx++) _canvasStack.Pop(); } public void PushClip(Geometry clipGeometry) { Canvas canvas = ActiveCanvas; if (canvas.Children.Count > 0 || canvas.Clip != null) canvas = PushCanvas(); canvas.Clip = clipGeometry; } //public void PushEffect(BitmapEffect effect, BitmapEffectInput effectInput); //public void PushGuidelineSet(GuidelineSet guidelines); public void PushOpacity(double opacity) { Canvas canvas = ActiveCanvas; if (canvas.Children.Count > 0 || !DoubleUtil.IsNaN(canvas.Opacity)) canvas = PushCanvas(); canvas.Opacity = opacity; } //public void PushOpacity(double opacity, AnimationClock opacityAnimations); public void PushOpacityMask(Brush opacityMask) { Canvas canvas = ActiveCanvas; if (canvas.Children.Count > 0 || canvas.OpacityMask != null) canvas = PushCanvas(); canvas.OpacityMask = opacityMask; } public void PushTransform(MatrixTransform transform) { Canvas canvas = ActiveCanvas; if (canvas.Children.Count > 0 || canvas.RenderTransform != null) canvas = PushCanvas(); canvas.RenderTransform = transform; } /// /// Resembles the DrawString function of GDI+. /// [Obsolete("Text may be drawn at the wrong position. Requires update!")] public void DrawString(XGraphics gfx, string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format) { double x = layoutRectangle.X; double y = layoutRectangle.Y; double lineSpace = font.GetHeight(); //old: font.GetHeight(gfx); double cyAscent = lineSpace * font.CellAscent / font.CellSpace; double cyDescent = lineSpace * font.CellDescent / font.CellSpace; bool bold = (font.Style & XFontStyle.Bold) != 0; bool italic = (font.Style & XFontStyle.Italic) != 0; bool strikeout = (font.Style & XFontStyle.Strikeout) != 0; bool underline = (font.Style & XFontStyle.Underline) != 0; //Debug.Assert(font.GlyphTypeface != null); TextBlock textBlock = new TextBlock(); //FontHelper.CreateTextBlock(text, font.GlyphTypeface, font.Size, brush.RealizeWpfBrush()); if (layoutRectangle.Width > 0) textBlock.Width = layoutRectangle.Width; switch (format.Alignment) { case XStringAlignment.Near: textBlock.TextAlignment = TextAlignment.Left; break; case XStringAlignment.Center: textBlock.TextAlignment = TextAlignment.Center; break; case XStringAlignment.Far: textBlock.TextAlignment = TextAlignment.Right; break; } if (gfx.PageDirection == XPageDirection.Downwards) { switch (format.LineAlignment) { case XLineAlignment.Near: //y += cyAscent; break; case XLineAlignment.Center: // TODO use CapHeight. PDFlib also uses 3/4 of ascent y += (layoutRectangle.Height - textBlock.ActualHeight) / 2; //y += -formattedText.Baseline + (font.Size * font.Metrics.CapHeight / font.unitsPerEm / 2) + layoutRectangle.Height / 2; break; case XLineAlignment.Far: y += layoutRectangle.Height - textBlock.ActualHeight; //y -= textBlock.ActualHeight; //-formattedText.Baseline - cyDescent + layoutRectangle.Height; break; case XLineAlignment.BaseLine: //#if !WINDOWS_PHONE y -= textBlock.BaselineOffset; //#else // // No BaselineOffset in Silverlight WP yet. // //y -= textBlock.BaselineOffset; //#endif break; } } else { throw new NotImplementedException("XPageDirection.Downwards"); } //if (bold && !descriptor.IsBoldFace) //{ // // TODO: emulate bold by thicker outline //} //if (italic && !descriptor.IsBoldFace) //{ // // TODO: emulate italic by shearing transformation //} if (underline) textBlock.TextDecorations = TextDecorations.Underline; // No strikethrough in Silverlight //if (strikeout) //{ // formattedText.SetTextDecorations(TextDecorations.Strikethrough); // //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace; // //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace; // //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); //} Canvas.SetLeft(textBlock, x); Canvas.SetTop(textBlock, y); ActiveCanvas.Children.Add(textBlock); } ///// ///// Resembles the MeasureString function of GDI+. ///// //[Obsolete("Use XGraphics.MeasureString()")] //public XSize MeasureString(XGraphics gfx, string text, XFont font, XStringFormat stringFormat) //{ // //Debug.Assert(font.GlyphTypeface != null); // TextBlock textBlock = new TextBlock(); //FontHelper.CreateTextBlock(text, font.GlyphTypeface, font.Size, null); // // Looks very much like a hack, but is the recommended way documented by Microsoft. // return new XSize(textBlock.ActualWidth, textBlock.ActualHeight); //} /// /// Create new canvas and add it to the children of the current canvas. /// internal Canvas PushCanvas() { Canvas canvas = new Canvas(); ActiveCanvas.Children.Add(canvas); _canvasStack.Push(canvas); return canvas; } /// /// Gets the currently active canvas. /// Canvas ActiveCanvas { get { return _canvasStack.Peek(); } } /// /// Gets the nesting level of Canvas objects. /// internal int Level { get { return _canvasStack.Count; } } readonly Stack _canvasStack = new Stack(); } } #endif