using FastReport.Utils; using System; using System.Collections.Generic; using System.Drawing; using System.Globalization; using System.IO; using System.Text; namespace FastReport.Export.Html { /// /// Represents the HTML export filter. /// public partial class HTMLExport : ExportBase { /// /// Draw any custom controls /// public event EventHandler CustomDraw; /// /// Draw any custom controls. /// /// private void OnCustomDraw(CustomDrawEventArgs e) { if (CustomDraw != null) { CustomDraw(this, e); } } #region Private fields #if READONLY_STRUCTS private readonly struct HTMLData #else private struct HTMLData #endif { public readonly int ReportPage; public readonly int PageNumber; public readonly int CurrentPage; public readonly ReportPage page; public readonly Stream PagesStream; public HTMLData(int reportPage, int pageNumber, int currentPage, ReportPage page, Stream pagesStream) { ReportPage = reportPage; PageNumber = pageNumber; CurrentPage = currentPage; this.page = page; PagesStream = pagesStream; } } #if READONLY_STRUCTS private readonly struct PicsArchiveItem #else private struct PicsArchiveItem #endif { public readonly string FileName; public readonly MemoryStream Stream; public PicsArchiveItem(string fileName, MemoryStream stream) { FileName = fileName; Stream = stream; } } /// /// Types of html export /// public enum ExportType { /// /// Simple export /// Export, /// /// Web preview mode /// WebPreview, /// /// Web print mode /// WebPrint } private bool layers; private bool wysiwyg; private bool notRotateLandscapePage; private bool highQualitySVG; private readonly MyRes res; private readonly HtmlTemplates templates; private string targetPath; private string targetIndexPath; private string targetFileName; private string fileName; private string navFileName; //private string FOutlineFileName; private int pagesCount; private string documentTitle; private ImageFormat imageFormat; private bool subFolder; private bool navigator; private bool singlePage; private bool pictures; private bool embedPictures; private bool webMode; private List pages; private HTMLPageData printPageData; private int count; private string webImagePrefix; private string webImageSuffix; private string stylePrefix; private string prevWatermarkName; private long prevWatermarkSize; private HtmlSizeUnits widthUnits; private HtmlSizeUnits heightUnits; private string singlePageFileName; private string subFolderPath; private HTMLExportFormat format; private MemoryStream mimeStream; private String boundary; private List picsArchive; private List prevStyleList; private int prevStyleListIndex; private bool pageBreaks; private bool print; private bool preview; private readonly List cssStyles; private float hPos; private string pageStyleName; private bool saveStreams; private string onClickTemplate = String.Empty; private string reportID; private const string BODY_BEGIN = "\r\n"; private const string BODY_END = ""; private const string PRINT_JS = ""; private const string NBSP = " "; private int currentPage = 0; private HTMLData d; private IGraphics htmlMeasureGraphics; private float maxWidth, maxHeight; private readonly FastString css = new FastString(); private readonly FastString htmlPage = new FastString(); private float leftMargin, topMargin; private bool enableMargins = false; private ExportType exportMode; private bool enableVectorObjects = true; /// /// hash:base64Image /// private Dictionary embeddedImages; #endregion Private fields #region Public properties /// /// Gets or sets images, embedded in html (hash:base64Image) /// public Dictionary EmbeddedImages { get { return embeddedImages; } set { embeddedImages = value; } } /// /// Sets a ID of report /// public string ReportID { get { return reportID; } set { reportID = value; } } /// /// Sets an onclick template /// public string OnClickTemplate { get { return onClickTemplate; } set { onClickTemplate = value; } } /// /// Enable or disable layers export mode /// public bool Layers { get { return layers; } set { layers = value; } } /// /// For internal use only. /// public string StylePrefix { get { return stylePrefix; } set { stylePrefix = value; } } /// /// For internal use only. /// public string WebImagePrefix { get { return webImagePrefix; } set { webImagePrefix = value; } } /// /// For internal use only. /// public string WebImageSuffix { get { return webImageSuffix; } set { webImageSuffix = value; } } /// /// For internal use only. /// public int Count { get { return count; } } /// /// For internal use only. /// public List PreparedPages { get { return pages; } } /// /// Enable or disable showing of print dialog in browser when html document is opened /// public bool Print { get { return print; } set { print = value; } } /// /// Enable or disable a picture optimization. /// public bool HighQualitySVG { get { return highQualitySVG; } set { highQualitySVG = value; } } /// /// Enable or disable preview in Web settings /// public bool Preview { get { return preview; } set { preview = value; } } /// /// Enable or disable the breaks between pages in print preview when single page mode is enabled /// public bool PageBreaks { get { return pageBreaks; } set { pageBreaks = value; } } /// /// Specifies the output format /// public HTMLExportFormat Format { get { return format; } set { format = value; } } /// /// Specifies the width units in HTML export /// public HtmlSizeUnits WidthUnits { get { return widthUnits; } set { widthUnits = value; } } /// /// Specifies the height units in HTML export /// public HtmlSizeUnits HeightUnits { get { return heightUnits; } set { heightUnits = value; } } /// /// Enable or disable the pictures in HTML export /// public bool Pictures { get { return pictures; } set { pictures = value; } } /// /// Enable or disable embedding pictures in HTML export /// public bool EmbedPictures { get { return embedPictures; } set { embedPictures = value; } } /// /// Enable or disable the WEB mode in HTML export /// internal bool WebMode { get { return webMode; } set { webMode = value; } } /// /// Gets or sets html export mode /// public ExportType ExportMode { get { return exportMode; } set { exportMode = value; } } /// /// Enable or disable the single HTML page creation /// public bool SinglePage { get { return singlePage; } set { singlePage = value; } } /// /// Enable or disable the page navigator in html export /// public bool Navigator { get { return navigator; } set { navigator = value; } } /// /// Enable or disable the sub-folder for files of export /// public bool SubFolder { get { return subFolder; } set { subFolder = value; } } /// /// Gets or sets the WYSIWYG quality of export /// public bool Wysiwyg { get { return wysiwyg; } set { wysiwyg = value; } } /// /// Gets or sets the image format. /// public ImageFormat ImageFormat { get { return imageFormat; } set { imageFormat = value; } } /// /// Gets print page data /// public HTMLPageData PrintPageData { get { return printPageData; } } /// /// Enable or disable saving streams in GeneratedStreams collection. /// public bool SaveStreams { get { return saveStreams; } set { saveStreams = value; } } /// /// Enable or disable margins for pages. Works only for Layers-mode. /// public bool EnableMargins { get { return enableMargins; } set { enableMargins = value; } } /// /// Enable or disable export of vector objects such as Barcodes in SVG format. /// public bool EnableVectorObjects { get { return enableVectorObjects; } set { enableVectorObjects = value; } } /// /// Not rotate landscape page when print. /// public bool NotRotateLandscapePage { get { return notRotateLandscapePage; } set { notRotateLandscapePage = value; } } #endregion Public properties #region Private methods private void GeneratedUpdate(string filename, Stream stream) { int i = GeneratedFiles.IndexOf(filename); if (i == -1) { GeneratedFiles.Add(filename); GeneratedStreams.Add(stream); } else { GeneratedStreams[i] = stream; } } private void ExportHTMLPageStart(FastString Page, int PageNumber, int CurrentPage) { if (webMode) { if (!layers) { pages[CurrentPage].CSSText = Page.ToString(); Page.Clear(); } pages[CurrentPage].PageNumber = PageNumber; } if (!webMode && !singlePage) { Page.AppendLine(BODY_BEGIN); } } private void ExportHTMLPageFinal(FastString CSS, FastString Page, HTMLData d, float MaxWidth, float MaxHeight) { if (!webMode) { if (!singlePage) Page.AppendLine(BODY_END); if (d.PagesStream == null) { string pageFileName = targetIndexPath + targetFileName + d.PageNumber.ToString() + ".html"; if (saveStreams) { string fileName = singlePage ? singlePageFileName : pageFileName; int i = GeneratedFiles.IndexOf(fileName); Stream outStream = (i == -1) ? new MemoryStream() : GeneratedStreams[i]; DoPage(outStream, documentTitle, CSS, Page); GeneratedUpdate(fileName, outStream); } else { GeneratedFiles.Add(pageFileName); using (FileStream outStream = new FileStream(pageFileName, FileMode.Create)) { DoPage(outStream, documentTitle, CSS, Page); } } } else { DoPage(d.PagesStream, documentTitle, CSS, Page); } } else { ExportHTMLPageFinalWeb(CSS, Page, d, MaxWidth, MaxHeight); } } private void ExportHTMLPageFinalWeb(FastString CSS, FastString Page, HTMLData d, float MaxWidth, float MaxHeight) { CalcPageSize(pages[d.CurrentPage], MaxWidth, MaxHeight); if (Page != null) { pages[d.CurrentPage].PageText = Page.ToString(); Page.Clear(); } if (CSS != null) { pages[d.CurrentPage].CSSText = CSS.ToString(); CSS.Clear(); } pages[d.CurrentPage].PageEvent.Set(); } private void CalcPageSize(HTMLPageData page, float MaxWidth, float MaxHeight) { if (!layers) { page.Width = MaxWidth / Zoom; page.Height = MaxHeight / Zoom; } else { page.Width = MaxWidth; page.Height = MaxHeight; } } private void DoPage(Stream stream, string documentTitle, FastString CSS, FastString Page) { if (!singlePage) ExportUtils.Write(stream, String.Format(templates.PageTemplateTitle, documentTitle)); if (CSS != null) { ExportUtils.Write(stream, CSS.ToString()); CSS.Clear(); } if (Page != null) { ExportUtils.Write(stream, Page.ToString()); Page.Clear(); } if (!singlePage) ExportUtils.Write(stream, templates.PageTemplateFooter); } private void ExportHTMLOutline(Stream OutStream) { if (!webMode) { // under construction } else { // under construction } } private void DoPageStart(Stream stream, string documentTitle, bool print) { ExportUtils.Write(stream, String.Format(templates.PageTemplateTitle, documentTitle)); if (print) ExportUtils.WriteLn(stream, PRINT_JS); ExportUtils.WriteLn(stream, BODY_BEGIN); } private void DoPageEnd(Stream stream) { ExportUtils.WriteLn(stream, BODY_END); ExportUtils.Write(stream, templates.PageTemplateFooter); } private void ExportHTMLIndex(Stream stream) { ExportUtils.Write(stream, String.Format(templates.IndexTemplate, documentTitle, ExportUtils.HtmlURL(navFileName), ExportUtils.HtmlURL(targetFileName + (singlePage ? ".main" : "1") + ".html"))); } private void ExportHTMLNavigator(Stream stream) { string prefix = ExportUtils.HtmlURL(fileName); // {0} - pages count {1} - name of report {2} multipage document {3} prefix of pages // {4} first caption {5} previous caption {6} next caption {7} last caption // {8} total caption ExportUtils.Write(stream, String.Format(templates.NavigatorTemplate, pagesCount.ToString(), documentTitle, (singlePage ? "0" : "1"), prefix, res.Get("First"), res.Get("Prev"), res.Get("Next"), res.Get("Last"), res.Get("Total"))); } private void Init() { htmlMeasureGraphics = Report.MeasureGraphics; cssStyles.Clear(); hPos = 0; count = Report.PreparedPages.Count; pagesCount = 0; prevWatermarkName = String.Empty; prevWatermarkSize = 0; prevStyleList = null; prevStyleListIndex = 0; picsArchive = new List(); GeneratedStreams = new List(); } private void StartMHT() { subFolder = false; singlePage = true; navigator = false; mimeStream = new MemoryStream(); boundary = ExportUtils.GetID(); } private void StartWeb() { pages.Clear(); for (int i = 0; i < count; i++) pages.Add(new HTMLPageData()); } private void StartSaveStreams() { if (singlePage) GeneratedUpdate("index.html", null); subFolder = false; navigator = false; } private void FinishMHT() { DoPageEnd(mimeStream); WriteMHTHeader(Stream, FileName); WriteMimePart(mimeStream, "text/html", "utf-8", "index.html"); for (int i = 0; i < picsArchive.Count; i++) { string imagename = picsArchive[i].FileName; WriteMimePart(picsArchive[i].Stream, "image/" + imagename.Substring(imagename.LastIndexOf('.') + 1), "utf-8", imagename); } string last = "--" + boundary + "--"; Stream.Write(Encoding.ASCII.GetBytes(last), 0, last.Length); } private void FinishSaveStreams() { // do append in memory stream int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); ExportHTMLIndex(GeneratedStreams[fileIndex]); MemoryStream outStream = new MemoryStream(); ExportHTMLNavigator(outStream); GeneratedUpdate(targetIndexPath + navFileName, outStream); } #endregion Private methods #region Protected methods /// protected override string GetFileFilter() { if (Format == HTMLExportFormat.HTML) return new MyRes("FileFilters").Get("HtmlFile"); else return new MyRes("FileFilters").Get("MhtFile"); } /// protected override void Start() { base.Start(); Init(); if (saveStreams) { StartSaveStreams(); } if (!webMode) { if (format == HTMLExportFormat.MessageHTML) { StartMHT(); } if (FileName == "" && Stream != null) { targetFileName = "html"; singlePage = true; subFolder = false; navigator = false; if (ExportMode == ExportType.WebPrint) { NotRotateLandscapePage = true; for (int i = 0; i < Report.PreparedPages.Count; i++) { if (!Report.PreparedPages.GetPage(i).Landscape) { NotRotateLandscapePage = false; break; } } } if (format == HTMLExportFormat.HTML && !embedPictures) pictures = false; } else { targetFileName = Path.GetFileNameWithoutExtension(FileName); fileName = targetFileName; targetIndexPath = !String.IsNullOrEmpty(FileName) ? Path.GetDirectoryName(FileName) : FileName; } if (!String.IsNullOrEmpty(targetIndexPath)) targetIndexPath += Path.DirectorySeparatorChar; if (preview) { pictures = true; printPageData = new HTMLPageData(); } else if (subFolder) { subFolderPath = targetFileName + ".files" + Path.DirectorySeparatorChar; targetPath = targetIndexPath + subFolderPath; targetFileName = subFolderPath + targetFileName; if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath); } else targetPath = targetIndexPath; navFileName = targetFileName + ".nav.html"; //FOutlineFileName = FTargetFileName + ".outline.html"; documentTitle = (!String.IsNullOrEmpty(Report.ReportInfo.Name) ? Report.ReportInfo.Name : Path.GetFileNameWithoutExtension(FileName)); if (singlePage) { if (navigator) { singlePageFileName = targetIndexPath + targetFileName + ".main.html"; if (saveStreams) { MemoryStream pageStream = new MemoryStream(); DoPageStart(pageStream, documentTitle, print); GeneratedUpdate(singlePageFileName, pageStream); } else { using (Stream pageStream = new FileStream(singlePageFileName, FileMode.Create)) { DoPageStart(pageStream, documentTitle, print); } } } else { singlePageFileName = String.IsNullOrEmpty(FileName) ? "index.html" : FileName; if (saveStreams) { GeneratedUpdate(singlePageFileName, new MemoryStream()); } DoPageStart((format == HTMLExportFormat.HTML) ? Stream : mimeStream, documentTitle, print); } } } else { StartWeb(); } } /// protected override void ExportPageBegin(ReportPage page) { if (ExportMode == ExportType.Export) base.ExportPageBegin(page); pagesCount++; if (!WebMode) { if (singlePage) { Stream stream; if (navigator) { if (saveStreams) stream = new MemoryStream(); else stream = new FileStream(singlePageFileName, FileMode.Append); d = new HTMLData(pagesCount, pagesCount, 0, page, stream); ExportHTMLPageBegin(d); } else { if (format == HTMLExportFormat.HTML) stream = Stream; else stream = mimeStream; d = new HTMLData(pagesCount, pagesCount, 0, page, stream); ExportHTMLPageBegin(d); } } else ProcessPageBegin(pagesCount - 1, pagesCount, page); } else // Web ProcessPageBegin(pagesCount - 1, pagesCount, page); } /// protected override void ExportPageEnd(ReportPage page) { if (!WebMode) { if (singlePage) { if (navigator) { if (saveStreams) { ExportHTMLPageEnd(d); GeneratedUpdate(singlePageFileName, d.PagesStream); } else { ExportHTMLPageEnd(d); d.PagesStream.Close(); d.PagesStream.Dispose(); } } else { ExportHTMLPageEnd(d); } } else ProcessPageEnd(pagesCount - 1, pagesCount); } else // Web ProcessPageEnd(pagesCount - 1, pagesCount); } /// /// Process Page with number p and real page ReportPage /// /// /// /// public void ProcessPageBegin(int p, int ReportPage, ReportPage page) { d = new HTMLData(ReportPage, pagesCount, p, page, null); ExportHTMLPageBegin(d); } /// /// Process Page with number p and real page ReportPage /// /// /// public void ProcessPageEnd(int p, int ReportPage) { ExportHTMLPageEnd(d); } /// protected override void Finish() { if (!webMode) { if (navigator) { if (saveStreams) { FinishSaveStreams(); } else { if (singlePage) { //if (saveStreams) // Commented because saveStreams is always false!! //{ // int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); // DoPageEnd(generatedStreams[fileIndex]); //} //else //{ using (Stream pageStream = new FileStream(singlePageFileName, FileMode.Append)) { DoPageEnd(pageStream); } //} // Commented because saveStreams is always false!! } ExportHTMLIndex(Stream); GeneratedFiles.Add(targetIndexPath + navFileName); using (FileStream outStream = new FileStream(targetIndexPath + navFileName, FileMode.Create)) { ExportHTMLNavigator(outStream); } //GeneratedFiles.Add(FTargetIndexPath + FOutlineFileName); //using (FileStream OutStream = new FileStream(FTargetIndexPath + FOutlineFileName, FileMode.Create)) // ExportHTMLOutline(OutStream); } } else if (format == HTMLExportFormat.MessageHTML) { FinishMHT(); } else { if (saveStreams) { if (!String.IsNullOrEmpty(singlePageFileName)) { int fileIndex = GeneratedFiles.IndexOf(singlePageFileName); DoPageEnd(GeneratedStreams[fileIndex]); } } else { if (!singlePage) { DoPageStart(Stream, documentTitle, false); int pageCounter = 0; foreach (string genFile in GeneratedFiles) { string ext = Path.GetExtension(genFile); if (ext == ".html" && genFile != FileName) { string file = Path.GetFileName(genFile); if (subFolder) file = Path.Combine(subFolderPath, file); ExportUtils.WriteLn(Stream, String.Format("Page {1}
", file, ++pageCounter)); } } } DoPageEnd(Stream); } } } } #endregion Protected methods /// public override void Serialize(FRWriter writer) { base.Serialize(writer); writer.WriteBool("Layers", Layers); writer.WriteBool("Wysiwyg", Wysiwyg); writer.WriteBool("Pictures", Pictures); writer.WriteBool("EmbedPictures", EmbedPictures); writer.WriteBool("SubFolder", SubFolder); writer.WriteBool("Navigator", Navigator); writer.WriteBool("SinglePage", SinglePage); writer.WriteBool("NotRotateLandscapePage", NotRotateLandscapePage); writer.WriteBool("HighQualitySVG", HighQualitySVG); } /// /// For internal use only. /// public void Init_WebMode() { subFolder = false; navigator = false; ShowProgress = false; pages = new List(); webMode = true; OpenAfterExport = false; } internal void Finish_WebMode() { pages.Clear(); } /// /// Initializes a new instance of the class. /// public HTMLExport() { Zoom = 1.0f; HasMultipleFiles = true; layers = true; wysiwyg = true; pictures = true; webMode = false; subFolder = true; navigator = true; singlePage = false; widthUnits = HtmlSizeUnits.Pixel; heightUnits = HtmlSizeUnits.Pixel; imageFormat = ImageFormat.Png; templates = new HtmlTemplates(); format = HTMLExportFormat.HTML; prevStyleList = null; prevStyleListIndex = 0; pageBreaks = true; print = false; preview = false; saveStreams = false; cssStyles = new List(); exportMode = ExportType.Export; res = new MyRes("Export,Html"); embeddedImages = new Dictionary(); notRotateLandscapePage = false; highQualitySVG = false; } /// /// Initializes a new instance of the class for WebPreview mode. /// public HTMLExport(bool webPreview) : this() { this.webPreview = webPreview; if (webPreview) exportMode = ExportType.WebPreview; } } /// /// Event arguments for custom drawing of report objects. /// public class CustomDrawEventArgs : EventArgs { /// /// Report object /// public Report report; /// /// ReportObject. /// public ReportComponentBase reportObject; /// /// Resulting successful drawing flag. /// public bool done = false; /// /// Resulting HTML string. /// public string html; /// /// Resulting CSS string. /// public string css; /// /// Layers mode when true or Table mode when false. /// public bool layers; /// /// Zoom value for scale position and sizes. /// public float zoom; /// /// Left position. /// public float left; /// /// Top position. /// public float top; /// /// Width of object. /// public float width; /// /// Height of object. /// public float height; } }