/// <summary> /// Parses a Path element. /// </summary> Path ParsePath() { Debug.Assert(this.reader.Name == "Path"); bool isEmptyElement = this.reader.IsEmptyElement; Path path = new Path(); while (MoveToNextAttribute()) { switch (this.reader.Name) { case "Data": path.Data = ParsePathGeometry(this.reader.Value); break; case "Fill": path.Fill = ParseBrush(this.reader.Value); break; case "RenderTransform": path.RenderTransform = ParseMatrixTransform(this.reader.Value); break; case "Clip": path.Clip = ParsePathGeometry(this.reader.Value); break; case "Opacity": path.Opacity = ParseDouble(this.reader.Value); break; case "OpacityMask": path.OpacityMask = ParseBrush(this.reader.Value); break; case "Stroke": path.Stroke = ParseBrush(this.reader.Value); break; case "StrokeDashArray": path.StrokeDashArray = reader.Value; break; case "StrokeDashCap": path.StrokeDashCap = reader.Value; break; case "StrokeDashOffset": path.StrokeDashOffset = ParseDouble(reader.Value); break; case "StrokeEndLineCap": path.StrokeEndLineCap = ParseEnum<LineCap>(this.reader.Value); break; case "StrokeStartLineCap": path.StrokeStartLineCap = ParseEnum<LineCap>(this.reader.Value); break; case "StrokeLineJoin": path.StrokeLineJoin = ParseEnum<LineJoin>(this.reader.Value); break; case "StrokeMiterLimit": path.StrokeMiterLimit = ParseDouble(this.reader.Value); break; case "StrokeThickness": path.StrokeThickness = ParseDouble(this.reader.Value); break; case "Name": path.Name = this.reader.Value; break; case "FixedPage_NavigateUri": case "FixedPage.NavigateUri": path.FixedPage_NavigateUri = reader.Value; break; case "AutomationProperties_Name": path.AutomationProperties_Name = this.reader.Value; break; case "AutomationProperties.HelpText": path.AutomationProperties_HelpText = this.reader.Value; break; case "SnapsToDevicePixels": path.SnapsToDevicePixels = ParseBool(this.reader.Value); break; case "xml:lang": path.lang = this.reader.Value; break; case "x:Key": path.Key = this.reader.Value; break; case "xml:id": break; case "xml:space": break; default: UnexpectedAttribute(this.reader.Name); break; } } if (!isEmptyElement) { MoveToNextElement(); while (this.reader.IsStartElement()) { switch (this.reader.Name) { case "Path.RenderTransform": MoveToNextElement(); path.RenderTransform = ParseMatrixTransform(); break; case "Path.Clip": MoveToNextElement(); path.Clip = ParsePathGeometry(); MoveToNextElement(); break; case "Path.OpacityMask": MoveToNextElement(); path.OpacityMask = ParseBrush(); path.OpacityMask.Parent = path; MoveToNextElement(); break; case "Path.Fill": MoveToNextElement(); path.Fill = ParseBrush(); path.Fill.Parent = path; MoveToNextElement(); break; case "Path.Stroke": MoveToNextElement(); path.Fill = ParseBrush(); path.Fill.Parent = path; MoveToNextElement(); break; case "Path.Data": MoveToNextElement(); path.Data = ParsePathGeometry(); MoveToNextElement(); break; default: Debugger.Break(); break; } } } MoveToNextElement(); return path; }
/// <summary> /// Strokes the path geometry with the Stroke brush. /// </summary> private void WriteStrokeGeometry(Path path) { if (path.Stroke == null) return; SolidColorBrush sBrush; LinearGradientBrush lgBrush; RadialGradientBrush rgBrush; VisualBrush vBrush; ImageBrush iBrush; //if (path.Stroke != null && this.renderMode == RenderMode.Default) // HACK if ((sBrush = path.Stroke as SolidColorBrush) != null) { RealizeStroke(path); WriteGeometry(path.Data); WriteLiteral("S\n"); } else if ((lgBrush = path.Stroke as LinearGradientBrush) != null) { PdfExtGState xgState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>(); xgState.SetDefault1(); double opacity = Opacity * lgBrush.Opacity; ; if (opacity < 1) { xgState.StrokeAlpha = opacity; xgState.NonStrokeAlpha = opacity; } RealizeExtGState(xgState); // /CS1 CS /P0 SCN // 7.5 w // q 1 0 0 1 15.5 462.9 cm // 0 0 m // 153 0 l // 153 -93 l // 0 -93 l // h // S // Q if (lgBrush.GradientStops.HasTransparency) { // TODO: Create Form PdfShadingPattern pattern = LinearShadingBuilder.BuildPatternFromLinearGradientBrush(Context, lgBrush, Transform); string paName = Resources.AddPattern(pattern); WriteLiteral("/Pattern CS " + paName + " SCN\n"); WriteLiteral("q {0:0.###} w", path.StrokeThickness); WriteGeometry(path.Data); WriteLiteral("S Q\n"); //// Create a FormXObject with a soft mask //PdfFormXObject form = LinearShadingBuilder.BuildFormFromLinearGradientBrush(Context, lgBrush, path.Data); //string foName = Resources.AddForm(form); //WriteLiteral(foName + " Do\n"); } else { PdfShadingPattern pattern = LinearShadingBuilder.BuildPatternFromLinearGradientBrush(Context, lgBrush, Transform); string paName = Resources.AddPattern(pattern); WriteLiteral("/Pattern CS " + paName + " SCN\n"); WriteLiteral("q {0:0.###} w", path.StrokeThickness); WriteGeometry(path.Data); WriteLiteral("S Q\n"); } } else if ((rgBrush = path.Stroke as RadialGradientBrush) != null) { // HACK WriteLiteral("/DeviceRGB CS 0 1 0 RG\n"); WriteLiteral("q {0:0.###} w", path.StrokeThickness); WriteGeometry(path.Data); WriteLiteral("S Q\n"); } else if ((iBrush = path.Stroke as ImageBrush) != null) { // HACK WriteLiteral("/DeviceRGB CS 0 1 0 RG\n"); WriteLiteral("q {0:0.###} w", path.StrokeThickness); WriteGeometry(path.Data); WriteLiteral("S Q\n"); } else if ((vBrush = path.Stroke as VisualBrush) != null) { // HACK WriteLiteral("/DeviceRGB CS 0 1 0 RG\n"); WriteLiteral("q {0:0.###} w", path.StrokeThickness); WriteGeometry(path.Data); WriteLiteral("S Q\n"); } else Debug.Assert(false); }
/// <summary> /// Writes the path fill and/or stroke operators to the content stream. /// </summary> internal void WritePathFillStroke(Path path) { if (path.Data.FillRule == FillRule.NonZero) // NonZero means Winding { if (path.Fill != null && path.Stroke != null) WriteLiteral("B\n"); else if (path.Stroke != null) WriteLiteral("S\n"); else WriteLiteral("f\n"); } else { if (path.Fill != null && path.Stroke != null) WriteLiteral("B*\n"); else if (path.Stroke != null) WriteLiteral("S\n"); else WriteLiteral("f*\n"); } }
/// <summary> /// Writes a Path to the content stream. /// </summary> private void WritePath(Path path) { WriteSaveState("begin Path", path.Name); // Transform also affects clipping and opacity mask if (path.RenderTransform != null && this.renderMode == RenderMode.Default) { MultiplyTransform(path.RenderTransform); WriteRenderTransform(path.RenderTransform); } if (path.Clip != null && this.renderMode == RenderMode.Default) WriteClip(path.Clip); if (path.Opacity < 1) MultiplyOpacity(path.Opacity); if (path.OpacityMask != null) WriteOpacityMask(path.OpacityMask); if (path.Fill == null) { if (path.Stroke != null) { #if true WriteStrokeGeometry(path); #else // Just stroke the path RealizeStroke(path); WriteGeometry(path.Data); WritePathFillStroke(path); #endif } else Debug.Assert(false, "??? Path with neither Stroke nor Fill encountered."); } else { SolidColorBrush sBrush; LinearGradientBrush lgBrush; RadialGradientBrush rgBrush; ImageBrush iBrush; VisualBrush vBrush; if ((sBrush = path.Fill as SolidColorBrush) != null) { Color color = sBrush.Color; double opacity = Opacity * color.ScA; if (opacity < 1) RealizeFillOpacity(opacity); WriteRgb(color, " rg\n"); if (path.Stroke != null) RealizeStroke(path); WriteGeometry(path.Data); WritePathFillStroke(path); } else if ((lgBrush = path.Fill as LinearGradientBrush) != null) { // TODO: For better visual compatibility use a Shading Pattern if Opacity is not 1 and // the Stroke Style is not solid. Acrobat 8 ignores this case. PdfExtGState xgState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>(); xgState.SetDefault1(); double opacity = Opacity * lgBrush.Opacity; ; if (opacity < 1) { xgState.StrokeAlpha = opacity; xgState.NonStrokeAlpha = opacity; } RealizeExtGState(xgState); // 1st draw fill if (lgBrush.GradientStops.HasTransparency) { // Create a FormXObject with a soft mask PdfFormXObject form = LinearShadingBuilder.BuildFormFromLinearGradientBrush(Context, lgBrush, path.Data); string foName = Resources.AddForm(form); WriteLiteral(foName + " Do\n"); } else { // Create just a shading PdfShading shading = LinearShadingBuilder.BuildShadingFromLinearGradientBrush(Context, lgBrush); string shName = Resources.AddShading(shading); WriteLiteral("q\n"); WriteClip(path.Data); WriteLiteral("BX " + shName + " sh EX Q\n"); } // 2nd draw stroke if (path.Stroke != null) WriteStrokeGeometry(path); } else if ((rgBrush = path.Fill as RadialGradientBrush) != null) { PdfExtGState xgState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>(); xgState.SetDefault1(); double avGradientOpacity = rgBrush.GradientStops.GetAverageAlpha(); // HACK double opacity = Opacity * rgBrush.Opacity * avGradientOpacity; if (opacity < 1) { xgState.StrokeAlpha = opacity; xgState.NonStrokeAlpha = opacity; } //RealizeExtGState(xgState); #if true XRect boundingBox = path.Data.GetBoundingBox(); // Always creates a pattern, because the background must be filled PdfShadingPattern pattern = RadialShadingBuilder.BuildFromRadialGradientBrush(Context, rgBrush, boundingBox, Transform); string paName = Resources.AddPattern(pattern); // stream // /CS0 cs /P0 scn // /GS0 gs // 0 480 180 -90 re // f* // endstream // endobj WriteLiteral("/Pattern cs " + paName + " scn\n"); // move to here: RealizeExtGState(xgState); RealizeExtGState(xgState); WriteGeometry(path.Data); if (path.Data.FillRule == FillRule.NonZero) // NonZero means Winding WriteLiteral("f\n"); else WriteLiteral("f*\n"); #else #if true // 1st draw fill if (rgBrush.GradientStops.HasTransparentColors) { // Create a FormXObject with a soft mask PdfFormXObject form = ShadingBuilder.BuildFormFromRadialGradientBrush(Context, rgBrush, path.Data); string foName = Resources.AddForm(form); WriteLiteral(foName + " Do\n"); } else { // Create just a shading PdfShading shading = ShadingBuilder.BuildShadingFromRadialGradientBrush(Context, rgBrush); string shName = Resources.AddShading(shading); WriteLiteral("q\n"); WriteClip(path.Data); WriteLiteral("BX " + shName + " sh EX Q\n"); } #else // Establish graphic state dictionary PdfExtGState extGState = new PdfExtGState(Context.PdfDocument); Context.PdfDocument.Internals.AddObject(extGState); string gsName = Resources.AddExtGState(extGState); WriteLiteral(gsName + " gs\n"); // 1st draw fill PdfShading shading = ShadingBuilder.BuildFormFromRadialGradientBrush(Context, rgBrush); //, extGState); string shName = Resources.AddShading(shading); WriteClip(path.Data); WriteLiteral("BX " + shName + " sh EX\n"); #endif #endif // 2nd draw stroke if (path.Stroke != null) WriteStrokeGeometry(path); } else if ((iBrush = path.Fill as ImageBrush) != null) { PdfExtGState xgState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>(); xgState.SetDefault1(); double opacity = Opacity * iBrush.Opacity; if (opacity < 1) { xgState.StrokeAlpha = opacity; xgState.NonStrokeAlpha = opacity; } RealizeExtGState(xgState); // 1st draw fill PdfTilingPattern pattern = TilingPatternBuilder.BuildFromImageBrush(Context, iBrush, Transform); string name = Resources.AddPattern(pattern); WriteLiteral("/Pattern cs " + name + " scn\n"); WriteGeometry(path.Data); WritePathFillStroke(path); // 2nd draw stroke if (path.Stroke != null) WriteStrokeGeometry(path); } else if ((vBrush = path.Fill as VisualBrush) != null) { PdfExtGState xgState = Context.PdfDocument.Internals.CreateIndirectObject<PdfExtGState>(); xgState.SetDefault1(); double opacity = Opacity * vBrush.Opacity; if (opacity < 1) { xgState.StrokeAlpha = opacity; xgState.NonStrokeAlpha = opacity; } RealizeExtGState(xgState); // 1st draw fill PdfTilingPattern pattern = TilingPatternBuilder.BuildFromVisualBrush(Context, vBrush, Transform); string name = Resources.AddPattern(pattern); WriteLiteral("/Pattern cs " + name + " scn\n"); WriteGeometry(path.Data); WritePathFillStroke(path); // 2nd draw stroke if (path.Stroke != null) WriteStrokeGeometry(path); } else { Debug.Assert(false, "Unknown brush type encountered."); } } WriteRestoreState("end Path", path.Name); }
/// <summary> /// Makes the specified pen to the current graphics object. /// </summary> public void RealizeStroke(Path path) { this.graphicsState.RealizeStroke(path); }
void AddCapToPath(Path path, PathFigure figure, double length, double lineWidthHalf, LineCap lineCap, XMatrix matrix) { // sketch: // 1. create Transform that make a horizontal line with start in 0,0 // 2. create a Polygon with the shape of the line including its caps // 3. render the shape with the brush of the pen //PolyLineSegment seg; switch (lineCap) { case LineCap.Flat: matrix.Transform(new XPoint(length + lineWidthHalf, -lineWidthHalf)); break; case LineCap.Square: matrix.Transform(new XPoint(length + lineWidthHalf, -lineWidthHalf)); break; case LineCap.Round: break; case LineCap.Triangle: break; } }
/// <summary> /// If the path is a single line with different start and end caps, convert the line into an area. /// </summary> private bool WriteSingleLineStrokeWithSpecialCaps(Path path) { if (path.StrokeStartLineCap == path.StrokeEndLineCap && path.StrokeStartLineCap != LineCap.Triangle) return false; if (path.Data.Figures.Count != 1) return false; PathFigure figure = path.Data.Figures[0]; if (figure.Segments.Count != 1) return false; PolyLineSegment polyLineSegment = figure.Segments[0] as PolyLineSegment; if (polyLineSegment.Points.Count != 1) return false; // TODO: Create a new path that draws the line path.GetType(); return false; }
/// <summary> /// Parses a Canvas element. /// </summary> Canvas ParseCanvas() { Debug.Assert(this.reader.Name == "Canvas"); Canvas canvas = new Canvas(); try { bool isEmptyElement = this.reader.IsEmptyElement; while (MoveToNextAttribute()) { switch (this.reader.Name) { case "Name": canvas.Name = this.reader.Value; break; case "RenderTransform": canvas.RenderTransform = ParseMatrixTransform(this.reader.Value); break; case "Clip": canvas.Clip = ParsePathGeometry(this.reader.Value); break; case "Opacity": canvas.Opacity = ParseDouble(this.reader.Value); break; case "OpacityMask": canvas.OpacityMask = ParseBrush(this.reader.Value); break; case "RenderOptions.EdgeMode": canvas.RenderOptions_EdgeMode = this.reader.Value; break; case "FixedPage.NavigateUri": canvas.FixedPage_NavigateUri = this.reader.Value; break; case "AutomationProperties.HelpText": canvas.AutomationProperties_HelpText = this.reader.Value; break; case "AutomationProperties.Name": canvas.AutomationProperties_Name = this.reader.Value; break; case "xml:lang": canvas.lang = this.reader.Value; break; case "xmlns:x": break; default: UnexpectedAttribute(this.reader.Name); break; } } if (!isEmptyElement) { MoveToNextElement(); while (this.reader.IsStartElement()) { switch (this.reader.Name) { case "Canvas.Resources": MoveToNextElement(); ResourceDictionary rd = new ResourceDictionary(); canvas.Resources = rd; rd.Parent = canvas; rd.ResourceParent = ResourceDictionaryStack.Current; ResourceDictionaryStack.Push(rd); ParseResourceDictionary(rd); MoveToNextElement(); break; case "Canvas.RenderTransform": MoveToNextElement(); canvas.RenderTransform = ParseMatrixTransform(); MoveToNextElement(); break; case "Canvas.Clip": MoveToNextElement(); canvas.Clip = ParsePathGeometry(); break; case "Canvas.OpacityMask": MoveToNextElement(); canvas.OpacityMask = ParseBrush(); canvas.OpacityMask.Parent = canvas; break; case "Path": { PdfSharp.Xps.XpsModel.Path path = ParsePath(); #if DEBUG if (!String.IsNullOrEmpty(path.Name)) { Debug.WriteLine("Path: " + path.Name); } #endif canvas.Content.Add(path); path.Parent = canvas; } break; case "Glyphs": { PdfSharp.Xps.XpsModel.Glyphs glyphs = ParseGlyphs(); canvas.Content.Add(glyphs); glyphs.Parent = canvas; } break; case "Canvas": { PdfSharp.Xps.XpsModel.Canvas canvas2 = ParseCanvas(); canvas.Content.Add(canvas2); canvas2.Parent = canvas; } break; case "mc:AlternateContent": case "mc:Choice": MoveToNextElement(); //canvas.Content.Add(ParseCanvas()); break; default: Debugger.Break(); break; } } } MoveToNextElement(); } finally { // If the current ResourceDictionary is from this Canvas, pop it. if (canvas != null && canvas.Resources != null) { if (Object.ReferenceEquals(canvas.Resources, ResourceDictionaryStack.Current)) { ResourceDictionaryStack.Pop(); } } } return(canvas); }