PDFsharp & MigraDoc Forum

PDFsharp - A .NET library for processing PDF & MigraDoc - Creating documents on the fly
It is currently Tue Mar 10, 2026 1:20 am

All times are UTC


Forum rules


Please read this before posting on this forum: Forum Rules

Also see our new Tailored Support & Services site.



Post new topic Reply to topic  [ 2 posts ] 
Author Message
 Post subject: SVG in MigraDoc
PostPosted: Tue Apr 26, 2016 8:39 pm 
Offline

Joined: Tue Apr 26, 2016 8:24 pm
Posts: 20
Hello I'm new to everything related to PdfSharp or MigraDoc.

I work on a company that generates reports automatically containing mainly titles , simple text , tables and SVG images. My intention is to replace the actual bad implementation with a new better and faster one and as far as I searched MigraDoc seems like a very good candidate (to be mentioned that the reports must come in different formats , HTML, RTF, DOC, but mainly PDF).


After looking on samples and thru the code I couldn't figure out how to add SVG files in MigraDoc.

I even saw posts about this like :
viewtopic.php?f=2&t=3265 <- he uses "DrawSvgElement(gfx, SvgElement); // I draw a Svg Element with XGraphics method." but the code isn't shared

or

viewtopic.php?f=3&t=2612 <- but XImage.FromFile throws error "Additional information: No imaging component suitable to complete this operation was found."

My question is: How can I add a svg file to MigraDoc section or paragraph?


Top
 Profile  
Reply with quote  
 Post subject: Re: SVG in MigraDoc
PostPosted: Mon Mar 09, 2026 3:24 pm 
Offline

Joined: Mon Mar 09, 2026 3:02 pm
Posts: 1
I also wondered how to render SVG (or vector graphics in general) in PDFsharp and MigraDoc, and did not find any working solution. Checking the source code it seems that only bitmap images are supported in general.

But I also saw that XImage has a derived class XPdfForm, which is basically a PDF which can be used as image (vector-based).

So I ended up creating some code to render common SVG functionality to a XGraphics, and create a single-page PDF file with a custom page size matching the SVG dimensions. This PDF can then be added as XImage to MigraDoc.

This approach works fine for simple vector graphics such as logos, your mileage may vary with more complex things.

Here's some code, using the "Svg" NuGet package (https://github.com/svg-net/SVG) and PDFsharp of course:

Code:
   public class PdfSharpSvgRenderer: ISvgRenderer {
      private readonly Stack<ISvgBoundable> boundables = new();
      private readonly Graphics dcGraphics = Graphics.FromHwnd(IntPtr.Zero);
      private readonly XGraphics gfx;
      private readonly Stack<XGraphicsState> states = new();

      public PdfSharpSvgRenderer(XGraphics gfx) {
         this.gfx = gfx;
      }

      public float DpiY => 72f;

      public SmoothingMode SmoothingMode {
         get;
         set;
      }

      public Matrix Transform {
         get {
            // Hack - getting the transform is usually used to save the state along with GetClip()
            return new Matrix();
         }
         set {
            if (!value.IsIdentity) {
               // Careful - XMatrix is a mutable struct, the local variables must not be inlined!
               var currentMatrix = this.gfx.Transform;
               currentMatrix.Invert();
               var delta = (XMatrix)value;
               delta.Multiply(currentMatrix, XMatrixOrder.Append);
               this.gfx.MultiplyTransform(delta);
            }
         }
      }

      public void Dispose() {
         this.dcGraphics.Dispose();
      }

      public void SetBoundable(ISvgBoundable boundable) {
         this.boundables.Push(boundable);
      }

      public ISvgBoundable GetBoundable() {
         return this.boundables.Count > 0
               ? this.boundables.Peek()
               : null;
      }

      public ISvgBoundable PopBoundable() {
         return this.boundables.Pop();
      }

      public void DrawPath(Pen pen, GraphicsPath path) {
         var xp = ConvertPath(path);
         var xpen = ConvertPen(pen);
         this.gfx.DrawPath(xpen, xp);
      }

      public void FillPath(Brush brush, GraphicsPath path) {
         var xp = ConvertPath(path);
         var xb = ConvertBrush(brush);
         this.gfx.DrawPath(null, xb, xp);
      }

      public void DrawImage(
            Image image,
            RectangleF destRect,
            RectangleF srcRect,
            GraphicsUnit graphicsUnit) {
         DrawImageInternal(image, destRect);
      }

      public void DrawImage(
            Image image,
            RectangleF destRect,
            RectangleF srcRect,
            GraphicsUnit graphicsUnit,
            float opacity) {
         DrawImageInternal(image, destRect);
      }

      public void DrawImageUnscaled(Image image, Point location) {
         var rect = new RectangleF(
               location.X,
               location.Y,
               image.Width,
               image.Height);
         DrawImageInternal(image, rect);
      }

      public void TranslateTransform(
            float dx,
            float dy,
            MatrixOrder order) {
         this.gfx.TranslateTransform(dx, dy);
      }

      public void ScaleTransform(
            float sx,
            float sy,
            MatrixOrder order) {
         this.gfx.ScaleTransform(sx, sy);
      }

      public void RotateTransform(
            float angle,
            MatrixOrder order) {
         this.gfx.RotateTransform(angle);
      }

      public void SetClip(Region region, CombineMode combineMode) {
         if (region == null) {
            // Restore clipping via saved state, see GetClip()
            Debug.Assert(combineMode == CombineMode.Replace);
            Restore();
         } else {
            this.gfx.IntersectClip(region.GetBounds(dcGraphics));
         }
      }

      public Region GetClip() {
         // Hack to manage clipping and transforms; GetClip is called to save the state and SetClip will later be called to restore the state
         Save();
         return null;
      }

      public void Save() {
         this.states.Push(this.gfx.Save());
      }

      public void Restore() {
         if (this.states.Count > 0) {
            this.gfx.Restore(this.states.Pop());
         }
      }

      public void DrawImage(Image image, RectangleF destRect) {
         DrawImageInternal(image, destRect);
      }

      private void DrawImageInternal(Image image, RectangleF rect) {
         using var ms = new MemoryStream();
         image.Save(ms, ImageFormat.Png);
         ms.Position = 0;
         using var ximg = XImage.FromStream(ms);
         this.gfx.DrawImage(
               ximg,
               rect.X,
               rect.Y,
               rect.Width,
               rect.Height);
      }

      public void DrawString(
            string text,
            Font font,
            Brush brush,
            PointF location) {
         var xf = new XFont(font.Name, font.Size);
         var xb = ConvertBrush(brush);
         this.gfx.DrawString(
               text,
               xf,
               xb,
               location.X,
               location.Y);
      }

      public SizeF MeasureString(string text, Font font) {
         var xf = new XFont(font.Name, font.Size);
         var s = this.gfx.MeasureString(text, xf);
         return new SizeF((float)s.Width, (float)s.Height);
      }

      public void SetClip(GraphicsPath path) {
         this.gfx.IntersectClip(ConvertPath(path));
      }

      public void ResetClip() {
         throw new NotImplementedException();
      }

      private XPen ConvertPen(Pen pen) {
         if (pen == null) {
            return null;
         }
         return new XPen(
               XColor.FromArgb(
                     pen.Color.A,
                     pen.Color.R,
                     pen.Color.G,
                     pen.Color.B),
               pen.Width);
      }

      private XBrush ConvertBrush(Brush brush) {
         if (brush is SolidBrush sb) {
            return new XSolidBrush(
                  XColor.FromArgb(
                        sb.Color.A,
                        sb.Color.R,
                        sb.Color.G,
                        sb.Color.B));
         }
         return XBrushes.Black;
      }

      private XGraphicsPath ConvertPath(GraphicsPath gp) {
         var xp = new XGraphicsPath {
               FillMode = gp.FillMode == FillMode.Alternate
                     ? XFillMode.Alternate
                     : XFillMode.Winding
         };
         var pts = gp.PathPoints;
         var types = gp.PathTypes;
         var i = 0;
         while (i < pts.Length) {
            var type = (PathPointType)(types[i]&(byte)PathPointType.PathTypeMask);
            var close = (types[i]&(byte)PathPointType.CloseSubpath) != 0;
            switch (type) {
            case PathPointType.Start:
               xp.StartFigure();
               i++;
               break;
            case PathPointType.Line:
               var lp0 = pts[i-1];
               var lp1 = pts[i];
               xp.AddLine(
                     lp0.X, lp0.Y,
                     lp1.X, lp1.Y);
               if (close) {
                  xp.CloseFigure();
               }
               i++;
               break;
            case PathPointType.Bezier3:
               if (i+2 >= pts.Length) {
                  break;
               }
               var bp0 = pts[i-1];
               var bc1 = pts[i];
               var bc2 = pts[i+1];
               var bp1 = pts[i+2];
               xp.AddBezier(
                     bp0.X, bp0.Y,
                     bc1.X, bc1.Y,
                     bc2.X, bc2.Y,
                     bp1.X, bp1.Y);
               var closeBezier = (types[i+2]&(byte)PathPointType.CloseSubpath) != 0;
               if (closeBezier) {
                  xp.CloseFigure();
               }
               i += 3;
               break;
            default:
               i++;
               break;
            }
         }
         return xp;
      }
   }


A couple of helper methods which leverage this code for creating a "base64:..." string which can be used as image string:

Code:
public static class SvgPdfSharpExtensions {
      public static void RenderSvgToPdf(this SvgDocument svg, Stream stream) {
         var viewport = svg.GetViewport();
         using var pdf = new PdfDocument();
         var page = pdf.AddPage();
         page.Width = XUnit.FromPoint(viewport.Width);
         page.Height = XUnit.FromPoint(viewport.Height);
         using var gfx = XGraphics.FromPdfPage(page);
         svg.RenderSvg(gfx, new XRect(default, viewport));
         pdf.Save(stream, false);
      }

      public static string RenderSvgToPdfBase64(this SvgDocument svg) {
         using var stream = new MemoryStream();
         svg.RenderSvgToPdf(stream);
         return "base64:" + Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length, Base64FormattingOptions.None);
      }

      private static XSize GetViewport(this SvgDocument svg) {
         if (svg.Width.IsEmpty || svg.Width.IsNone || svg.Width.Type == SvgUnitType.Percentage) {
            svg.Width = new SvgUnit(SvgUnitType.Pixel, svg.ViewBox.Width);
         }
         if (svg.Height.IsEmpty || svg.Height.IsNone || svg.Height.Type == SvgUnitType.Percentage) {
            svg.Height = new SvgUnit(SvgUnitType.Pixel, svg.ViewBox.Height);
         }
         var width = svg.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, null);
         var height = svg.Height.ToDeviceValue(null, UnitRenderingType.Vertical, null);
         return new XSize(width, height);
      }

      public static void RenderSvg(this SvgDocument svg, XGraphics gfx, XRect targetRect) {
         using var renderer = new PdfSharpSvgRenderer(gfx);
         gfx.Save();
         ApplyViewBoxTransform(gfx, svg, targetRect);
         svg.Draw(renderer);
         gfx.Restore();
      }

      private static void ApplyViewBoxTransform(XGraphics gfx, SvgDocument svg, XRect rect) {
         if (svg.ViewBox.Width <= 0 || svg.ViewBox.Height <= 0) {
            // fallback: scale using width/height
            gfx.TranslateTransform(rect.X, rect.Y);
            gfx.ScaleTransform(rect.Width / svg.Width.Value, rect.Height / svg.Height.Value);
            return;
         }
         var vb = svg.ViewBox;
         // Default preserveAspectRatio = xMidYMid meet
         var scale = Math.Min(rect.Width / vb.Width, rect.Height / vb.Height);
         var scaledWidth = vb.Width * scale;
         var scaledHeight = vb.Height * scale;
         var offsetX = rect.X+(rect.Width-scaledWidth) / 2;
         var offsetY = rect.Y+(rect.Height-scaledHeight) / 2;
         gfx.TranslateTransform(offsetX, offsetY);
         gfx.ScaleTransform(scale);
         gfx.TranslateTransform(-vb.MinX, -vb.MinY);
      }
   }


I hope someone else looking for vector graphiocs support in PDFsharp and MigraDoc finds this useful.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 2 posts ] 

All times are UTC


Who is online

Users browsing this forum: GPT and 416 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Privacy Policy, Data Protection Declaration, Impressum
Powered by phpBB® Forum Software © phpBB Group