using System.Collections.Generic;
using System.IO;
using System.Collections;
using System.Drawing.Imaging;
using FastReport.Utils;
using System.Drawing;
namespace FastReport.Export.Svg
{
///
/// Represents the SVG export filter.
///
public partial class SVGExport : ExportBase
{
private XmlDocument doc;
#region Private Fields
private string path;
private string fileNameWOext;
private string extension;
private string pageFileName;
private SvgGraphics graphics;
private int currentPage;
private bool pictures;
private bool embedPictures;
private bool useWidthAndHeight;
private bool useViewBox;
private AspectRatio preserveAspectRatio;
private string prefixStyle;
private float pageWidth;
private float pageHeight;
#endregion
#region Properties
///
/// Enable or disable the pictures in SVG export
///
public bool Pictures
{
get { return pictures; }
set { pictures = value; }
}
///
/// Gets or sets the image format used when exporting.
///
public SVGImageFormat ImageFormat { get; set; }
///
/// Embed images into svg
///
public bool EmbedPictures
{
get { return embedPictures; }
set { embedPictures = value; }
}
///
/// Gets or sets value indicating whether or not should to force uniform scaling of SVG document
///
public AspectRatio PreserveAspectRatio
{
get { return preserveAspectRatio; }
set { preserveAspectRatio = value; }
}
///
/// Gets or sets value indicating whether or not should be added 'viewBox' attribute to the svg tag
///
public bool UseViewBox
{
get { return useViewBox; }
set { useViewBox = value; }
}
///
/// Gets or sets value indicating whether or not should be added 'width' and 'height' attributes to the svg tag
///
public bool UseWidthAndHeight
{
get { return useWidthAndHeight; }
set { useWidthAndHeight = value; }
}
///
/// Gets or sets the prefix for style classes and object ids
///
public string PrefixStyle
{
get { return prefixStyle; }
set { prefixStyle = value; }
}
#endregion
#region Private XML Methods
private void Save(string filename)
{
doc.Save(filename);
}
private void Save(Stream stream)
{
doc.Save(stream);
}
private void CreateDoc(string imagePathAndPrefix, int pageNo = 0)
{
doc = new XmlDocument();
doc.AutoIndent = true;
if (prefixStyle == null)
prefixStyle = string.Empty;
graphics = new SvgGraphics(doc);
graphics.PrefixStyle = prefixStyle + pageNo + "_";
graphics.SvgImageFormat = this.ImageFormat;
graphics.EmbeddedImages = this.EmbedPictures;
if (!EmbedPictures)
graphics.ImageFilePrefix = imagePathAndPrefix;
}
private void UpdateSize(float width, float height)
{
if (useWidthAndHeight)
graphics.Size = new SizeF(width, height);
if (useViewBox)
graphics.ViewBox = new RectangleF(0, 0, width, height);
if (preserveAspectRatio != null)
{
FastString ratioVal = new FastString(preserveAspectRatio.Align.ToString());
if (preserveAspectRatio.MeetOrSlice != null)
{
ratioVal.Append(" ").Append(preserveAspectRatio.MeetOrSlice.ToString());
}
doc.Root.SetProp("preserveAspectRatio", ratioVal.ToString());
}
}
private void AddImageWatermark(ReportPage page)
{
page.Watermark.DrawImage(new FRPaintEventArgs(graphics, 1, 1, Report.GraphicCache),
new RectangleF(-page.LeftMargin * Units.Millimeters, -page.TopMargin * Units.Millimeters, page.WidthInPixels, page.HeightInPixels),
page.Report, false);
}
private void AddTextWatermark(ReportPage page)
{
if (string.IsNullOrEmpty(page.Watermark.Text))
return;
page.Watermark.DrawText(new FRPaintEventArgs(graphics, 1, 1, Report.GraphicCache),
new RectangleF(-page.LeftMargin * Units.Millimeters, -page.TopMargin * Units.Millimeters, page.WidthInPixels, page.HeightInPixels),
page.Report, false);
}
#endregion
#region Protected Methods
///
protected override void Start()
{
base.Start();
GeneratedStreams = new List();
currentPage = 0;
pageWidth = 0;
pageHeight = 0;
if (FileName != "" && FileName != null)
{
path = Path.GetDirectoryName(FileName);
fileNameWOext = Path.GetFileNameWithoutExtension(FileName);
extension = Path.GetExtension(FileName);
}
else
{
path = "";
fileNameWOext = "svgreport";
}
if (!HasMultipleFiles)
CreateDoc(Path.Combine(path, fileNameWOext));
}
///
/// Begin exporting of page
///
///
protected override void ExportPageBegin(ReportPage page)
{
base.ExportPageBegin(page);
if (path != null && path != "")
pageFileName = Path.Combine(path, fileNameWOext + currentPage.ToString() + extension);
else
pageFileName = null;
if (HasMultipleFiles)
{
CreateDoc(Path.Combine(path, fileNameWOext + currentPage.ToString()), currentPage);
UpdateSize(page.WidthInPixels, page.HeightInPixels);
}
graphics.TranslateTransform(page.LeftMargin * Units.Millimeters, page.TopMargin * Units.Millimeters);
// export bottom watermark
if (page.Watermark.Enabled && !page.Watermark.ShowImageOnTop)
AddImageWatermark(page);
if (page.Watermark.Enabled && !page.Watermark.ShowTextOnTop)
AddTextWatermark(page);
// create a bookmark as a hyperlink target with kind is page number
XmlItem xml = graphics.OpenContainer("a");
xml.SetProp("id", $"page{currentPage + 1}");
graphics.CloseContainer();
}
///
protected override void ExportBand(BandBase band)
{
List spanCoords = new List();
base.ExportBand(band);
ExportObj(band);
foreach (Base c in band.AllObjects)
{
if (c is Table.TableCell)
{
Table.TableCell cell = (c as Table.TableCell);
if (spanCoords.Contains(cell.Address))
continue;
for (int i = 1; i < cell.ColSpan; i++)
spanCoords.Add(new Point(cell.Address.X + i, cell.Address.Y));
for (int i = 1; i < cell.RowSpan; i++)
spanCoords.Add(new Point(cell.Address.X, cell.Address.Y + i));
}
else
ExportObj(c);
}
}
private void ExportObj(Base obj)
{
if (obj is ReportComponentBase reportComponent && reportComponent.Exportable)
{
if (!string.IsNullOrEmpty(reportComponent.Hyperlink.Value) || !string.IsNullOrEmpty(reportComponent.Bookmark))
{
XmlItem xml = graphics.OpenContainer(!string.IsNullOrEmpty(reportComponent.Hyperlink.Value) ? "a" : "g");
if (!string.IsNullOrEmpty(reportComponent.Bookmark))
xml.SetProp("id", reportComponent.Bookmark);
if (!string.IsNullOrEmpty(reportComponent.Hyperlink.Value))
{
switch (reportComponent.Hyperlink.Kind)
{
case HyperlinkKind.URL:
xml.SetProp("href", reportComponent.Hyperlink.Value.ToString());
break;
case HyperlinkKind.PageNumber:
xml.SetProp("href", $"#page{reportComponent.Hyperlink.Value}");
break;
case HyperlinkKind.DetailPage:
case HyperlinkKind.DetailReport:
break;
case HyperlinkKind.Bookmark:
xml.SetProp("href", $"#{reportComponent.Hyperlink.Value}");
break;
default:
xml.SetProp("href", reportComponent.Hyperlink.Value.ToString());
break;
}
if (reportComponent.Hyperlink.OpenLinkInNewTab)
xml.SetProp("target", "_blank");
}
}
reportComponent.Draw(new FRPaintEventArgs(graphics, 1, 1, Report.GraphicCache));
if (!string.IsNullOrEmpty(reportComponent.Hyperlink.Value) || !string.IsNullOrEmpty(reportComponent.Bookmark))
graphics.CloseContainer();
}
}
///
/// End exporting
///
///
protected override void ExportPageEnd(ReportPage page)
{
base.ExportPageEnd(page);
// export top watermark
if (page.Watermark.Enabled && page.Watermark.ShowImageOnTop)
AddImageWatermark(page);
if (page.Watermark.Enabled && page.Watermark.ShowTextOnTop)
AddTextWatermark(page);
if (HasMultipleFiles)
{
if (Directory.Exists(path) && !string.IsNullOrEmpty(FileName))
{
// desktop mode
if (currentPage == 0)
{
// save first page in parent Stream
Save(Stream);
Stream.Position = 0;
GeneratedStreams.Add(Stream);
GeneratedFiles.Add(FileName);
}
else
{
// save all pages after first in files
Save(pageFileName);
GeneratedFiles.Add(pageFileName);
}
}
else if (string.IsNullOrEmpty(path))
{
// server mode, save in internal stream collection
if (currentPage == 0)
{
// save first page in parent Stream
Save(Stream);
Stream.Position = 0;
GeneratedStreams.Add(Stream);
GeneratedFiles.Add(FileName);
}
else
{
MemoryStream pageStream = new MemoryStream();
Save(pageStream);
pageStream.Position = 0;
GeneratedStreams.Add(pageStream);
GeneratedFiles.Add(pageFileName);
}
}
}
else
{
graphics.TranslateTransform(-page.LeftMargin * Units.Millimeters, -page.TopMargin * Units.Millimeters + page.HeightInPixels);
if (pageWidth < page.WidthInPixels)
pageWidth = page.WidthInPixels;
pageHeight += page.HeightInPixels;
}
// increment page number
currentPage++;
}
///
protected override void Finish()
{
if (!HasMultipleFiles)
{
UpdateSize(pageWidth, pageHeight);
Save(Stream);
Stream.Position = 0;
GeneratedFiles.Add(FileName);
}
graphics.Dispose();
graphics = null;
doc.Dispose();
doc = null;
GeneratedFiles.Clear();
GeneratedStreams.Clear();
}
///
protected override string GetFileFilter()
{
return new MyRes("FileFilters").Get("SVGFile");
}
#endregion
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
writer.WriteValue("ImageFormat", ImageFormat);
writer.WriteBool("HasMultipleFiles", HasMultipleFiles);
writer.WriteValue("EmbedPictures", EmbedPictures);
}
///
/// Initializes a new instance of the class.
///
public SVGExport()
{
HasMultipleFiles = false;
pictures = true;
ImageFormat = SVGImageFormat.Png;
preserveAspectRatio = null;
useWidthAndHeight = true;
useViewBox = true;
prefixStyle = "p";
}
}
}