using System; using System.IO; using System.IO.Compression; using System.Xml; using System.Text; using System.Windows; using System.Windows.Media; using System.Windows.Media.Effects; using System.Windows.Media.Imaging; using SharpVectors.Dom.Svg; using SharpVectors.Runtime; using SharpVectors.Renderers; using SharpVectors.Renderers.Wpf; using SharpVectors.Renderers.Utils; namespace SharpVectors.Converters { /// /// This converts the SVG file to static or bitmap image, which is /// saved to a file. /// /// /// /// The image is save with the format, /// since that is the only pixel format which does not throw an exception /// with the . /// /// /// The DPI used is 96. /// /// public sealed class ImageSvgConverter : SvgConverter { #region Private Fields private bool _writerErrorOccurred; private bool _fallbackOnWriterError; private ImageEncoderType _encoderType; private BitmapEncoder _bitampEncoder; /// /// Required designer variable. /// private WpfSvgWindow _wpfWindow; private WpfDrawingRenderer _wpfRenderer; #endregion #region Constructors and Destructor /// /// Initializes a new instance of the class. /// /// /// Initializes a new instance of the class /// with the specified drawing or rendering settings. /// /// /// This specifies the settings used by the rendering or drawing engine. /// If this is , the default settings is used. /// public ImageSvgConverter(WpfDrawingSettings settings) : this(false, false, settings) { } /// /// Initializes a new instance of the class /// with the specified drawing or rendering settings and the saving options. /// /// /// This specifies whether to save result object tree in image file. /// /// /// This specifies whether to save result object tree in ZAML file. The /// ZAML is simply a G-Zip compressed image format, similar to the SVGZ. /// /// /// This specifies the settings used by the rendering or drawing engine. /// If this is , the default settings is used. /// public ImageSvgConverter(bool saveXaml, bool saveZaml, WpfDrawingSettings settings) : base(saveXaml, saveZaml, settings) { _encoderType = ImageEncoderType.PngBitmap; _wpfRenderer = new WpfDrawingRenderer(this.DrawingSettings); _wpfWindow = new WpfSvgWindow(640, 480, _wpfRenderer); } #endregion #region Public Properties /// /// Gets a value indicating whether a writer error occurred when /// using the custom image writer. /// /// /// This is if an error occurred when using /// the custom image writer; otherwise, it is . /// public bool WriterErrorOccurred { get { return _writerErrorOccurred; } } /// /// Gets or sets a value indicating whether to fall back and use /// the .NET Framework image writer when an error occurred in using the /// custom writer. /// /// /// This is if the converter falls back to using /// the system image writer when an error occurred in using the custom /// writer; otherwise, it is . If , /// an exception, which occurred in using the custom writer will be /// thrown. The default is . /// public bool FallbackOnWriterError { get { return _fallbackOnWriterError; } set { _fallbackOnWriterError = value; } } /// /// Gets or set the bitmap encoder type to use in encoding the drawing /// to an image file. /// /// /// An enumeration of the type specifying /// the bitmap encoder. The default is the . /// public ImageEncoderType EncoderType { get { return _encoderType; } set { _encoderType = value; } } /// /// Gets or sets a custom bitmap encoder to use in encoding the drawing /// to an image file. /// /// /// A derived object specifying the bitmap /// encoder for encoding the images. The default is , /// and the property determines the encoder used. /// /// /// If the value of this is set, it must match the MIME type or file /// extension defined by the property for it /// to be used. /// public BitmapEncoder Encoder { get { return _bitampEncoder; } set { _bitampEncoder = value; } } #endregion #region Public Methods /// /// This performs the conversion of the specified SVG file, and saves /// the output to an image file. /// /// /// This performs the conversion of the specified SVG file, and saves /// the output to an image file with the same file name. /// /// /// The full path of the SVG source file. /// /// /// This returns if the conversion is successful; /// otherwise, it return . /// /// /// If the is . /// /// /// If the is empty. /// -or- /// If the does not exists. /// public bool Convert(string svgFileName) { return this.Convert(svgFileName, String.Empty); } /// /// This performs the conversion of the specified SVG file, and saves /// the output to the specified image file. /// /// /// The full path of the SVG source file. /// /// /// The output image file. This is optional. If not specified, an image /// file is created in the same directory as the SVG file. /// /// /// This returns if the conversion is successful; /// otherwise, it return . /// /// /// If the is . /// /// /// If the is empty. /// -or- /// If the does not exists. /// public bool Convert(string svgFileName, string imageFileName) { if (svgFileName == null) { throw new ArgumentNullException("svgFileName", "The SVG source file cannot be null (or Nothing)."); } if (svgFileName.Length == 0) { throw new ArgumentException( "The SVG source file cannot be empty.", "svgFileName"); } if (!File.Exists(svgFileName)) { throw new ArgumentException( "The SVG source file must exists.", "svgFileName"); } if (String.IsNullOrEmpty(svgFileName) || !File.Exists(svgFileName)) { return false; } if (!String.IsNullOrEmpty(imageFileName)) { string workingDir = Path.GetDirectoryName(imageFileName); if (!Directory.Exists(workingDir)) { Directory.CreateDirectory(workingDir); } } return this.ProcessFile(svgFileName, imageFileName); } /// /// This performs the conversion of the specified SVG source, and saves /// the output to the specified image file. /// /// /// A stream providing access to the SVG source data. /// /// /// The output image file. This is optional. If not specified, an image /// file is created in the same directory as the SVG file. /// /// /// This returns if the conversion is successful; /// otherwise, it return . /// /// /// If the is . /// -or- /// If the is . /// /// /// If the is empty. /// public bool Convert(Stream svgStream, string imageFileName) { if (svgStream == null) { throw new ArgumentNullException("svgStream", "The SVG source file cannot be null (or Nothing)."); } if (imageFileName == null) { throw new ArgumentNullException("imageFileName", "The image destination file path cannot be null (or Nothing)."); } if (imageFileName.Length == 0) { throw new ArgumentException( "The image destination file path cannot be empty.", "imageFileName"); } if (!this.SaveXaml && !this.SaveZaml) { return false; } if (!String.IsNullOrEmpty(imageFileName)) { string workingDir = Path.GetDirectoryName(imageFileName); if (!Directory.Exists(workingDir)) { Directory.CreateDirectory(workingDir); } } return this.ProcessFile(svgStream, imageFileName); } /// /// This performs the conversion of the specified SVG source, and saves /// the output to the specified image file. /// /// /// A text reader providing access to the SVG source data. /// /// /// The output image file. This is optional. If not specified, an image /// file is created in the same directory as the SVG file. /// /// /// This returns if the conversion is successful; /// otherwise, it return . /// /// /// If the is . /// -or- /// If the is . /// /// /// If the is empty. /// public bool Convert(TextReader svgTextReader, string imageFileName) { if (svgTextReader == null) { throw new ArgumentNullException("svgTextReader", "The SVG source file cannot be null (or Nothing)."); } if (imageFileName == null) { throw new ArgumentNullException("imageFileName", "The image destination file path cannot be null (or Nothing)."); } if (imageFileName.Length == 0) { throw new ArgumentException( "The image destination file path cannot be empty.", "imageFileName"); } if (!this.SaveXaml && !this.SaveZaml) { return false; } if (!String.IsNullOrEmpty(imageFileName)) { string workingDir = Path.GetDirectoryName(imageFileName); if (!Directory.Exists(workingDir)) { Directory.CreateDirectory(workingDir); } } return this.ProcessFile(svgTextReader, imageFileName); } /// /// This performs the conversion of the specified SVG source, and saves /// the output to the specified image file. /// /// /// An XML reader providing access to the SVG source data. /// /// /// The output image file. This is optional. If not specified, an image /// file is created in the same directory as the SVG file. /// /// /// This returns if the conversion is successful; /// otherwise, it return . /// /// /// If the is . /// -or- /// If the is . /// /// /// If the is empty. /// public bool Convert(XmlReader svgXmlReader, string imageFileName) { if (svgXmlReader == null) { throw new ArgumentNullException("svgXmlReader", "The SVG source file cannot be null (or Nothing)."); } if (imageFileName == null) { throw new ArgumentNullException("imageFileName", "The image destination file path cannot be null (or Nothing)."); } if (imageFileName.Length == 0) { throw new ArgumentException( "The image destination file path cannot be empty.", "imageFileName"); } if (!this.SaveXaml && !this.SaveZaml) { return false; } if (!String.IsNullOrEmpty(imageFileName)) { string workingDir = Path.GetDirectoryName(imageFileName); if (!Directory.Exists(workingDir)) { Directory.CreateDirectory(workingDir); } } return this.ProcessFile(svgXmlReader, imageFileName); } #endregion #region Private Methods #region ProcessFile Method private bool ProcessFile(string fileName, string imageFileName) { _wpfRenderer.LinkVisitor = new LinkVisitor(); _wpfRenderer.ImageVisitor = new EmbeddedImageVisitor(); _wpfRenderer.FontFamilyVisitor = new FontFamilyVisitor(); _wpfWindow.LoadDocument(fileName); _wpfRenderer.InvalidRect = SvgRectF.Empty; _wpfRenderer.Render(_wpfWindow.Document as SvgDocument); DrawingGroup renderedDrawing = _wpfRenderer.Drawing as DrawingGroup; if (renderedDrawing == null) { return false; } // Save to the image file... SaveImageFile(renderedDrawing, fileName, imageFileName); // Save to image and/or ZAML file if required... if (this.SaveXaml || this.SaveZaml) { SaveXamlFile(renderedDrawing, fileName, imageFileName); } renderedDrawing = null; return true; } private bool ProcessFile(Stream svgStream, string imageFileName) { _wpfRenderer.LinkVisitor = new LinkVisitor(); _wpfRenderer.ImageVisitor = new EmbeddedImageVisitor(); _wpfRenderer.FontFamilyVisitor = new FontFamilyVisitor(); _wpfWindow.LoadDocument(svgStream); _wpfRenderer.InvalidRect = SvgRectF.Empty; _wpfRenderer.Render(_wpfWindow.Document as SvgDocument); DrawingGroup renderedDrawing = _wpfRenderer.Drawing as DrawingGroup; if (renderedDrawing == null) { return false; } // Save to the image file... SaveImageFile(renderedDrawing, imageFileName, imageFileName); // Save to image and/or ZAML file if required... if (this.SaveXaml || this.SaveZaml) { SaveXamlFile(renderedDrawing, imageFileName, imageFileName); } renderedDrawing = null; return true; } private bool ProcessFile(TextReader svgTextReader, string imageFileName) { _wpfRenderer.LinkVisitor = new LinkVisitor(); _wpfRenderer.ImageVisitor = new EmbeddedImageVisitor(); _wpfRenderer.FontFamilyVisitor = new FontFamilyVisitor(); _wpfWindow.LoadDocument(svgTextReader); _wpfRenderer.InvalidRect = SvgRectF.Empty; _wpfRenderer.Render(_wpfWindow.Document as SvgDocument); DrawingGroup renderedDrawing = _wpfRenderer.Drawing as DrawingGroup; if (renderedDrawing == null) { return false; } // Save to the image file... SaveImageFile(renderedDrawing, imageFileName, imageFileName); // Save to image and/or ZAML file if required... if (this.SaveXaml || this.SaveZaml) { SaveXamlFile(renderedDrawing, imageFileName, imageFileName); } renderedDrawing = null; return true; } private bool ProcessFile(XmlReader svgXmlReader, string imageFileName) { _wpfRenderer.LinkVisitor = new LinkVisitor(); _wpfRenderer.ImageVisitor = new EmbeddedImageVisitor(); _wpfRenderer.FontFamilyVisitor = new FontFamilyVisitor(); _wpfWindow.LoadDocument(svgXmlReader); _wpfRenderer.InvalidRect = SvgRectF.Empty; _wpfRenderer.Render(_wpfWindow.Document as SvgDocument); DrawingGroup renderedDrawing = _wpfRenderer.Drawing as DrawingGroup; if (renderedDrawing == null) { return false; } // Save to the image file... SaveImageFile(renderedDrawing, imageFileName, imageFileName); // Save to image and/or ZAML file if required... if (this.SaveXaml || this.SaveZaml) { SaveXamlFile(renderedDrawing, imageFileName, imageFileName); } renderedDrawing = null; return true; } #endregion #region SaveImageFile Method private bool SaveImageFile(Drawing drawing, string fileName, string imageFileName) { string outputExt = this.GetImageFileExtention(); string outputFileName = null; if (String.IsNullOrEmpty(imageFileName)) { string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName); string workingDir = Path.GetDirectoryName(fileName); outputFileName = Path.Combine(workingDir, fileNameWithoutExt + outputExt); } else { string fileExt = Path.GetExtension(imageFileName); if (String.IsNullOrEmpty(fileExt)) { outputFileName = imageFileName + outputExt; } else if (!String.Equals(fileExt, outputExt, StringComparison.OrdinalIgnoreCase)) { outputFileName = Path.ChangeExtension(imageFileName, outputExt); } } BitmapEncoder bitampEncoder = this.GetBitmapEncoder(outputExt); // The image parameters... Rect drawingBounds = drawing.Bounds; int pixelWidth = (int)drawingBounds.Width; int pixelHeight = (int)drawingBounds.Height; double dpiX = 96; double dpiY = 96; // The Visual to use as the source of the RenderTargetBitmap. DrawingVisualEx drawingVisual = new DrawingVisualEx(); DrawingContext drawingContext = drawingVisual.RenderOpen(); drawingContext.DrawDrawing(drawing); drawingContext.Close(); // The BitmapSource that is rendered with a Visual. RenderTargetBitmap targetBitmap = new RenderTargetBitmap( pixelWidth, pixelHeight, dpiX, dpiY, PixelFormats.Pbgra32); targetBitmap.Render(drawingVisual); // Encoding the RenderBitmapTarget as an image file. bitampEncoder.Frames.Add(BitmapFrame.Create(targetBitmap)); using (FileStream stream = File.Create(outputFileName)) { bitampEncoder.Save(stream); } return true; } private BitmapEncoder GetBitmapEncoder(string fileExtension) { BitmapEncoder bitampEncoder = null; if (_bitampEncoder != null && _bitampEncoder.CodecInfo != null) { string mimeType = String.Empty; BitmapCodecInfo codecInfo = _bitampEncoder.CodecInfo; string mimeTypes = codecInfo.MimeTypes; string fileExtensions = codecInfo.FileExtensions; switch (_encoderType) { case ImageEncoderType.BmpBitmap: mimeType = "image/bmp"; break; case ImageEncoderType.GifBitmap: mimeType = "image/gif"; break; case ImageEncoderType.JpegBitmap: mimeType = "image/jpeg,image/jpe,image/jpg"; break; case ImageEncoderType.PngBitmap: mimeType = "image/png"; break; case ImageEncoderType.TiffBitmap: mimeType = "image/tiff,image/tif"; break; case ImageEncoderType.WmpBitmap: mimeType = "image/vnd.ms-photo"; break; } if (!String.IsNullOrEmpty(fileExtensions) && fileExtensions.IndexOf(fileExtension, StringComparison.OrdinalIgnoreCase) >= 0) { bitampEncoder = _bitampEncoder; } else if (!String.IsNullOrEmpty(mimeTypes) && !String.IsNullOrEmpty(mimeType)) { string[] arrayMimeTypes = mimeType.Split(','); for (int i = 0; i < arrayMimeTypes.Length; i++) { if (mimeTypes.IndexOf(arrayMimeTypes[i], StringComparison.OrdinalIgnoreCase) >= 0) { bitampEncoder = _bitampEncoder; break; } } } } if (bitampEncoder == null) { switch (_encoderType) { case ImageEncoderType.BmpBitmap: bitampEncoder = new BmpBitmapEncoder(); break; case ImageEncoderType.GifBitmap: bitampEncoder = new GifBitmapEncoder(); break; case ImageEncoderType.JpegBitmap: JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder(); // Set the default/user options... bitampEncoder = jpgEncoder; break; case ImageEncoderType.PngBitmap: PngBitmapEncoder pngEncoder = new PngBitmapEncoder(); // Set the default/user options... bitampEncoder = pngEncoder; break; case ImageEncoderType.TiffBitmap: bitampEncoder = new TiffBitmapEncoder(); break; case ImageEncoderType.WmpBitmap: WmpBitmapEncoder wmpEncoder = new WmpBitmapEncoder(); // Set the default/user options... bitampEncoder = wmpEncoder; break; } } if (bitampEncoder == null) { bitampEncoder = new PngBitmapEncoder(); } return bitampEncoder; } private string GetImageFileExtention() { switch (_encoderType) { case ImageEncoderType.BmpBitmap: return ".bmp"; case ImageEncoderType.GifBitmap: return ".gif"; case ImageEncoderType.JpegBitmap: return ".jpg"; case ImageEncoderType.PngBitmap: return ".png"; case ImageEncoderType.TiffBitmap: return ".tif"; case ImageEncoderType.WmpBitmap: return ".wdp"; } return ".png"; } #endregion #region SaveXamlFile Method private bool SaveXamlFile(Drawing drawing, string fileName, string imageFileName) { _writerErrorOccurred = false; string xamlFileName = null; if (String.IsNullOrEmpty(imageFileName)) { string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName); string workingDir = Path.GetDirectoryName(fileName); xamlFileName = Path.Combine(workingDir, fileNameWithoutExt + ".xaml"); } else { string fileExt = Path.GetExtension(imageFileName); if (String.IsNullOrEmpty(fileExt)) { xamlFileName = imageFileName + ".xaml"; } else if (!String.Equals(fileExt, ".xaml", StringComparison.OrdinalIgnoreCase)) { xamlFileName = Path.ChangeExtension(imageFileName, ".xaml"); } } if (File.Exists(xamlFileName)) { File.SetAttributes(xamlFileName, FileAttributes.Normal); File.Delete(xamlFileName); } if (this.UseFrameXamlWriter) { XmlWriterSettings writerSettings = new XmlWriterSettings(); writerSettings.Indent = true; writerSettings.OmitXmlDeclaration = true; writerSettings.Encoding = Encoding.UTF8; using (FileStream xamlFile = File.Create(xamlFileName)) { using (XmlWriter writer = XmlWriter.Create( xamlFile, writerSettings)) { System.Windows.Markup.XamlWriter.Save( drawing, writer); } } } else { try { XmlXamlWriter xamlWriter = new XmlXamlWriter( this.DrawingSettings); using (FileStream xamlFile = File.Create(xamlFileName)) { xamlWriter.Save(drawing, xamlFile); } } catch { _writerErrorOccurred = true; if (_fallbackOnWriterError) { if (File.Exists(xamlFileName)) { File.Move(xamlFileName, xamlFileName + ".bak"); } XmlWriterSettings writerSettings = new XmlWriterSettings(); writerSettings.Indent = true; writerSettings.OmitXmlDeclaration = true; writerSettings.Encoding = Encoding.UTF8; using (FileStream xamlFile = File.Create(xamlFileName)) { using (XmlWriter writer = XmlWriter.Create( xamlFile, writerSettings)) { System.Windows.Markup.XamlWriter.Save( drawing, writer); } } } else { throw; } } } if (this.SaveZaml) { string zamlFileName = Path.ChangeExtension(xamlFileName, ".zaml"); if (File.Exists(zamlFileName)) { File.SetAttributes(zamlFileName, FileAttributes.Normal); File.Delete(zamlFileName); } FileStream zamlSourceFile = new FileStream(xamlFileName, FileMode.Open, FileAccess.Read, FileShare.Read); byte[] buffer = new byte[zamlSourceFile.Length]; // Read the file to ensure it is readable. int count = zamlSourceFile.Read(buffer, 0, buffer.Length); if (count != buffer.Length) { zamlSourceFile.Close(); return false; } zamlSourceFile.Close(); FileStream zamlDestFile = File.Create(zamlFileName); GZipStream zipStream = new GZipStream(zamlDestFile, CompressionMode.Compress, true); zipStream.Write(buffer, 0, buffer.Length); zipStream.Close(); zamlDestFile.Close(); } if (!this.SaveXaml && File.Exists(xamlFileName)) { File.Delete(xamlFileName); } return true; } #endregion #endregion #region DrawingVisualEx Class public sealed class DrawingVisualEx : DrawingVisual { public DrawingVisualEx() { } public Effect Effect { get { return this.VisualEffect; } set { this.VisualEffect = value; } } } #endregion } }