using System;
using System.Drawing;
using System.IO;
using FastReport.Table;
using FastReport.Utils;
using System.Windows.Forms;
using FastReport.Export;
using System.ComponentModel;
using System.Drawing.Text;
namespace FastReport.Export.Html
{
public partial class HTMLExport : ExportBase
{
private bool doPageBreak;
private void ExportPageStylesLayers(FastString styles, int PageNumber)
{
PrintPageStyle(styles);
if (prevStyleListIndex < cssStyles.Count)
{
styles.AppendLine(HTMLGetStylesHeader());
for (int i = prevStyleListIndex; i < cssStyles.Count; i++)
styles.Append(HTMLGetStyleHeader(i, PageNumber)).Append(cssStyles[i]).AppendLine("}");
styles.AppendLine(HTMLGetStylesFooter());
}
}
private void Layer(FastString Page, ReportComponentBase obj,
float Left, float Top, float Width, float Height, FastString Text, string classTag, FastString addstyletag)
{
if (Page != null && obj != null)
{
string onclick = null;
if (!String.IsNullOrEmpty(ReportID))
{
if (!String.IsNullOrEmpty(obj.ClickEvent) || obj.HasClickListeners())
{
onclick = "click";
}
CheckBoxObject checkBoxObject = obj as CheckBoxObject;
if (checkBoxObject != null && checkBoxObject.Editable)
{
onclick = "checkbox_click";
}
TextObject textObject = obj as TextObject;
if (textObject != null && textObject.Editable)
{
onclick = "text_edit";
}
}
// we need to adjust left, top, width and height values because borders take up space in html elements
float borderLeft;
float borderRight;
float borderTop;
float borderBottom;
HTMLBorderWidthValues(obj, out borderLeft, out borderTop, out borderRight, out borderBottom);
string href = GetHref(obj);
if (!string.IsNullOrEmpty(href))
{
Page.Append(href);
}
Page.Append("
");
if (Text == null)
Page.Append(NBSP);
else
Page.Append(Text);
Page.AppendLine("
");
if (!string.IsNullOrEmpty(href))
{
Page.Append("");
}
}
}
private string EncodeURL(string value)
{
#if CROSSPLATFORM || COREWIN
return System.Net.WebUtility.UrlEncode(value);
#else
return ExportUtils.HtmlURL(value);
#endif
}
private string GetHref(ReportComponentBase obj)
{
string href = String.Empty;
href = GetHrefAdvMatrixButton(obj, href);
if (!String.IsNullOrEmpty(obj.Hyperlink.Value))
{
string hrefStyle = String.Empty;
if (obj is TextObject)
{
TextObject textObject = obj as TextObject;
hrefStyle = String.Format("style=\"color:{0}{1}\"",
ExportUtils.HTMLColor(textObject.TextColor),
!textObject.Font.Underline ? ";text-decoration:none" : String.Empty
);
}
string url = EncodeURL(obj.Hyperlink.Value);
if (obj.Hyperlink.Kind == HyperlinkKind.URL)
href = String.Format("", hrefStyle, onClick);
}
}
return href;
}
private FastString GetSpanText(TextObjectBase obj, FastString text,
float top, float width,
float ParagraphOffset)
{
FastString style = new FastString();
style.Append("display:block;border:0;white-space: pre-wrap;width:").Append(Px(width * Zoom));
if (ParagraphOffset != 0)
style.Append("text-indent:").Append(Px(ParagraphOffset * Zoom));
if (obj.Padding.Left != 0)
style.Append("padding-left:").Append(Px(obj.Padding.Left * Zoom));
if (obj.Padding.Right != 0)
style.Append("padding-right:").Append(Px(obj.Padding.Right * Zoom));
if (obj.Padding.Top != 0)
style.Append("padding-top:").Append(Px(obj.Padding.Top * Zoom));
// if the vertical alignment is not equal to "at the top edge"
if (top != 0)
style.Append("margin-top:").Append(Px((top - obj.Padding.Top) * Zoom));
if (obj is TextObject textObject && !textObject.WordWrap)
style.Append("overflow: hidden; text-wrap: nowrap;");
// we need to apply border width in order to position our div perfectly
float borderLeft;
float borderRight;
float borderTop;
float borderBottom;
if (HTMLBorderWidthValues(obj, out borderLeft, out borderTop, out borderRight, out borderBottom))
{
style.Append("position:absolute;")
.Append("left:").Append(Px(-1 * borderLeft / 2f))
.Append("top:").Append(Px(-1 * borderTop / 2f));
}
FastString result = new FastString(128);
result.Append("").
Append(text).Append("
");
return result;
}
private void LayerText(FastString Page, TextObject obj)
{
float top = 0;
if (obj.Font.FontFamily.Name == "Wingdings" || obj.Font.FontFamily.Name == "Webdings")
{
obj.Text = WingdingsToUnicodeConverter.Convert(obj.Text);
}
switch (obj.TextRenderType)
{
case TextRenderType.HtmlParagraph:
using (HtmlTextRenderer htmlTextRenderer = obj.GetHtmlTextRenderer(Zoom, Zoom))
{
if (obj.VertAlign == VertAlign.Center)
{
top = (obj.Height - htmlTextRenderer.CalcHeight()) / 2;
}
else if (obj.VertAlign == VertAlign.Bottom)
{
top = obj.Height - htmlTextRenderer.CalcHeight();
}
FastString sb = GetHtmlParagraph(htmlTextRenderer);
LayerBack(Page, obj,
GetSpanText(obj, sb,
top + obj.Padding.Top,
obj.Width - obj.Padding.Horizontal,
obj.ParagraphOffset));
}
break;
default:
if (obj.VertAlign != VertAlign.Top)
{
IGraphics g = htmlMeasureGraphics;
using (Font f = new Font(obj.Font.FontFamily, obj.Font.Size * DrawUtils.ScreenDpiFX, obj.Font.Style))
{
RectangleF textRect = new RectangleF(obj.AbsLeft + obj.Padding.Left, obj.AbsTop + obj.Padding.Top,
obj.Width - obj.Padding.Left - obj.Padding.Right,
obj.Height - obj.Padding.Top - obj.Padding.Bottom);
StringFormat format = obj.GetStringFormat(Report.GraphicCache, 0);
Brush textBrush = Report.GraphicCache.GetBrush(obj.TextColor);
AdvancedTextRenderer renderer = new AdvancedTextRenderer(obj.Text, g, f, textBrush, null,
textRect, format, obj.HorzAlign, obj.VertAlign, obj.LineHeight, obj.Angle, obj.FontWidthRatio,
obj.ForceJustify, obj.Wysiwyg, obj.HasHtmlTags, false, Zoom, Zoom, obj.InlineImageCache);
if (renderer.Paragraphs.Count > 0)
{
if (renderer.Paragraphs[0].Lines.Count > 0)
{
float height = renderer.Paragraphs[0].Lines[0].CalcHeight();
if (height > obj.Height)
top = -(height - obj.Height) / 2;
else
{
top = renderer.Paragraphs[0].Lines[0].Top - obj.AbsTop;
height = renderer.CalcHeight();
if (obj.VertAlign == VertAlign.Center)
{
top = (obj.Height - height - obj.Padding.Bottom + obj.Padding.Top) / 2;
if (top < 0)
{
if (obj.Height > height)
top = (obj.Height - height - obj.Padding.Bottom + obj.Padding.Top) / 2;
else
top = (height - obj.Height - obj.Padding.Bottom + obj.Padding.Top) / 2;
}
}
else if (obj.VertAlign == VertAlign.Bottom)
{
// (float)(Math.Round(obj.Font.Size * 96 / 72) / 2
// necessary to compensate for paragraph offset error in GetSpanText method below
top = obj.Height - height - obj.Padding.Bottom - (float)(Math.Round(obj.Font.Size * 96 / 72) / 2);
}
}
}
}
}
}
LayerBack(Page, obj,
GetSpanText(obj, ExportUtils.HtmlString(obj.Text, obj.TextRenderType, Px(Math.Round(obj.Font.Size * 96 / 72))),
top,
obj.Width - obj.Padding.Horizontal,
obj.ParagraphOffset));
break;
}
}
private FastString GetHtmlParagraph(HtmlTextRenderer renderer)
{
FastString sb = new FastString();
bool isFirstLine;
foreach (HtmlTextRenderer.Paragraph paragraph in renderer.Paragraphs)
{
isFirstLine = true;
foreach (HtmlTextRenderer.Line line in paragraph.Lines)
{
if (sb == null) sb = new FastString();
sb.Append(" renderer.DisplayRect.Bottom)
sb.Append("height:").Append(Math.Max(renderer.DisplayRect.Bottom - line.Top, 0).ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
else
{
//sb.Append("height:").Append(line.Height.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
if (line.LineSpacing > 0)
{
sb.Append("margin-bottom:").Append(line.LineSpacing.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
}
}
if (!isFirstLine)
sb.Append("overflow:hidden;");
sb.Append("line-height:").Append(line.Height.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
if (line.HorzAlign == HorzAlign.Justify)
sb.Append("text-align-last:justify;");
else sb.Append("white-space:pre;");
sb.Append("\">");
HtmlTextRenderer.StyleDescriptor styleDesc = null;
float prevWidth = 0;
foreach (HtmlTextRenderer.Word word in line.Words)
{
foreach (HtmlTextRenderer.Run run in word.Runs)
{
if (!run.Style.FullEquals(styleDesc))
{
if (styleDesc != null)
styleDesc.ToHtml(sb, true);
styleDesc = run.Style;
styleDesc.ToHtml(sb, false);
}
if (run is HtmlTextRenderer.RunText)
{
HtmlTextRenderer.RunText runText = run as HtmlTextRenderer.RunText;
foreach (char ch in runText.Text)
{
switch (ch)
{
case '"':
sb.Append(""");
break;
case '&':
sb.Append("&");
break;
case '<':
sb.Append("<");
break;
case '>':
sb.Append(">");
break;
case '\t':
if (word.Type == HtmlTextRenderer.WordType.Tab)
{
if (layers)
sb.Append($"	");
else
sb.Append($"	");
}
else
sb.Append("	");
break;
default:
sb.Append(ch);
break;
}
}
}
else if (run is HtmlTextRenderer.RunImage)
{
HtmlTextRenderer.RunImage runImage = run as HtmlTextRenderer.RunImage;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
try
{
float w, h;
using (Bitmap bmp = runImage.GetBitmap(out w, out h))
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}
ms.Flush();
sb.Append("
");
}
catch (Exception /*e*/)
{
}
}
}
prevWidth += run.Width;
//run.ToHtml(sb, true);
}
}
if (styleDesc != null)
styleDesc.ToHtml(sb, true);
else sb.Append("
");
sb.Append("");
isFirstLine = false;
}
}
return sb;
}
private void LayerHtml(FastString page, HtmlObject obj)
{
LayerBack(page, obj,
GetSpanText(obj, new FastString(obj.Text),
obj.Padding.Top,
obj.Width - obj.Padding.Horizontal,
0));
}
private string GetLayerPicture(ReportComponentBase obj, out float Width, out float Height)
{
string result = String.Empty;
Width = 0;
Height = 0;
if (obj != null)
{
if (pictures)
{
MemoryStream PictureStream = new MemoryStream();
System.Drawing.Imaging.ImageFormat FPictureFormat = System.Drawing.Imaging.ImageFormat.Bmp;
if (imageFormat == ImageFormat.Png)
FPictureFormat = System.Drawing.Imaging.ImageFormat.Png;
else if (imageFormat == ImageFormat.Jpeg)
FPictureFormat = System.Drawing.Imaging.ImageFormat.Jpeg;
else if (imageFormat == ImageFormat.Gif)
FPictureFormat = System.Drawing.Imaging.ImageFormat.Gif;
Width = obj.Width == 0 ? obj.Border.LeftLine.Width : obj.Width;
Height = obj.Height == 0 ? obj.Border.TopLine.Width : obj.Height;
if (Math.Abs(Width) * Zoom < 1 && Zoom > 0)
Width = 1 / Zoom;
if (Math.Abs(Height) * Zoom < 1 && Zoom > 0)
Height = 1 / Zoom;
int zoom = highQualitySVG ? 3 : 1;
using (System.Drawing.Image image =
new Bitmap(
(int)(Math.Abs(Math.Round(Width * Zoom * zoom))),
(int)(Math.Abs(Math.Round(Height * Zoom * zoom)))
))
{
using (Graphics g = Graphics.FromImage(image))
{
var needClear = obj is TextObjectBase
#if MSCHART
|| obj is MSChart.MSChartObject
#endif
|| obj is Gauge.GaugeObject;
if (needClear)
{
g.Clear(imageFormat == ImageFormat.Bmp ? Color.White : Color.Transparent);
g.TextRenderingHint = TextRenderingHint.AntiAlias;
}
float Left = Width > 0 ? obj.AbsLeft : obj.AbsLeft + Width;
float Top = Height > 0 ? obj.AbsTop : obj.AbsTop + Height;
float dx = 0;
float dy = 0;
g.TranslateTransform((-Left - dx) * Zoom * zoom, (-Top - dy) * Zoom * zoom);
BorderLines oldLines = obj.Border.Lines;
obj.Border.Lines = BorderLines.None;
obj.Draw(new FRPaintEventArgs(g, Zoom * zoom, Zoom * zoom, Report.GraphicCache));
obj.Border.Lines = oldLines;
}
using (Bitmap b = new Bitmap(
(int)(Math.Abs(Math.Round(Width * Zoom))),
(int)(Math.Abs(Math.Round(Height * Zoom)))
))
{
using (Graphics gr = Graphics.FromImage(b))
{
gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
gr.DrawImage(image, 0, 0, (int)Math.Abs(Width) * Zoom, (int)Math.Abs(Height) * Zoom);
}
if (FPictureFormat == System.Drawing.Imaging.ImageFormat.Jpeg)
ExportUtils.SaveJpeg(b, PictureStream, 95);
else
b.Save(PictureStream, FPictureFormat);
}
}
PictureStream.Position = 0;
string hash = String.Empty;
if (obj is PictureObject)
{
PictureObject pic = (obj as PictureObject);
if (pic.Image != null)
{
#if MONO
using (MemoryStream picStr = new MemoryStream())
{
ImageHelper.Save(pic.Image, picStr);
using(StreamWriter picWriter = new StreamWriter(picStr))
{
picWriter.Write(pic.Width);
picWriter.Write(pic.Height);
picWriter.Write(pic.Angle);
picWriter.Write(pic.Transparency);
picWriter.Write(pic.TransparentColor.ToArgb());
picWriter.Write(pic.CanShrink);
picWriter.Write(pic.CanGrow);
hash = Crypter.ComputeHash(picStr);
}
}
#else
hash = Crypter.ComputeHash(PictureStream);
PictureStream.Position = 0;
#endif
}
}
else
hash = Crypter.ComputeHash(PictureStream);
result = HTMLGetImage(0, 0, 0, hash, true, null, PictureStream, false);
}
}
return result;
}
private void LayerPicture(FastString Page, ReportComponentBase obj, FastString text)
{
if (pictures)
{
string old_text = String.Empty;
if (IsMemo(obj))
{
old_text = (obj as TextObject).Text;
(obj as TextObject).Text = String.Empty;
}
float Width, Height;
string pic = GetLayerPicture(obj, out Width, out Height);
if (IsMemo(obj))
(obj as TextObject).Text = old_text;
FastString picStyleBuilder = new FastString("background: url('")
.Append(pic).Append("') no-repeat !important;-webkit-print-color-adjust:exact;");
string style = GetStyle(obj, picStyleBuilder.ToString());
//FastString addstyle = new FastString(128);
//addstyle.Append(" background: url('").Append(pic).Append("') no-repeat !important;-webkit-print-color-adjust:exact;");
//if (String.IsNullOrEmpty(text))
// text = NBSP;
float x = Width > 0 ? obj.AbsLeft : (obj.AbsLeft + Width);
float y = Height > 0 ? hPos + obj.AbsTop : (hPos + obj.AbsTop + Height);
Layer(Page, obj, x, y, Width, Height, text, style, null);
}
}
private void LayerShape(FastString Page, ShapeObject obj, FastString text)
{
float Width, Height;
FastString addstyle = new FastString(64);
addstyle.Append("position:absolute;");
addstyle.Append("background: url('" + GetLayerPicture(obj, out Width, out Height) + "');no-repeat !important;-webkit-print-color-adjust:exact;");
float x = obj.Width > 0 ? obj.AbsLeft : (obj.AbsLeft + obj.Width);
float y = obj.Height > 0 ? hPos + obj.AbsTop : (hPos + obj.AbsTop + obj.Height);
Layer(Page, obj, x, y, obj.Width, obj.Height, text, null, addstyle);
}
private void LayerBack(FastString Page, ReportComponentBase obj, FastString text)
{
if (obj.Border.Shadow)
{
using (TextObject shadow = new TextObject())
{
shadow.Left = obj.AbsLeft + obj.Border.ShadowWidth + obj.Border.LeftLine.Width;
shadow.Top = obj.AbsTop + obj.Height + obj.Border.BottomLine.Width;
shadow.Width = obj.Width + obj.Border.RightLine.Width;
shadow.Height = obj.Border.ShadowWidth + obj.Border.BottomLine.Width;
shadow.FillColor = obj.Border.ShadowColor;
shadow.Border.Lines = BorderLines.None;
LayerBack(Page, shadow, null);
shadow.Left = obj.AbsLeft + obj.Width + obj.Border.RightLine.Width;
shadow.Top = obj.AbsTop + obj.Border.ShadowWidth + obj.Border.TopLine.Width;
shadow.Width = obj.Border.ShadowWidth + obj.Border.RightLine.Width;
shadow.Height = obj.Height;
LayerBack(Page, shadow, null);
}
}
if (!(obj is PolyLineObject))
{
if (obj.Fill is SolidFill)
Layer(Page, obj, obj.AbsLeft, hPos + obj.AbsTop, obj.Width, obj.Height, text, GetStyle(obj), null);
else
LayerPicture(Page, obj, text);
}
}
private void LayerTable(FastString Page, FastString CSS, TableBase table)
{
float y = 0;
for (int i = 0; i < table.RowCount; i++)
{
float x = 0;
for (int j = 0; j < table.ColumnCount; j++)
{
if (!table.IsInsideSpan(table[j, i]))
{
TableCell textcell = table[j, i];
textcell.Left = x;
textcell.Top = y;
// custom draw
CustomDrawEventArgs e = new CustomDrawEventArgs();
e.report = Report;
e.reportObject = textcell;
e.layers = Layers;
e.zoom = Zoom;
e.left = textcell.AbsLeft;
e.top = hPos + textcell.AbsTop;
e.width = textcell.Width;
e.height = textcell.Height;
OnCustomDraw(e);
if (e.done)
{
CSS.Append(e.css);
Page.Append(e.html);
}
else
{
if (textcell is TextObject && !(textcell as TextObject).TextOutline.Enabled && IsMemo(textcell))
LayerText(Page, textcell as TextObject);
else
{
LayerBack(Page, textcell as ReportComponentBase, null);
LayerPicture(Page, textcell as ReportComponentBase, null);
}
}
}
x += (table.Columns[j]).Width;
}
y += (table.Rows[i]).Height;
}
}
private bool IsMemo(ReportComponentBase Obj)
{
if (Obj is TextObject)
{
TextObject aObj = Obj as TextObject;
return (aObj.Angle == 0) && (aObj.FontWidthRatio == 1) && (!aObj.TextOutline.Enabled) && (!aObj.Underlines);
}
return false;
}
private void Watermark(FastString Page, ReportPage page, bool drawText)
{
using (PictureObject pictureWatermark = new PictureObject())
{
pictureWatermark.Left = 0;
pictureWatermark.Top = 0;
pictureWatermark.Width = (ExportUtils.GetPageWidth(page) - page.LeftMargin - page.RightMargin) * Units.Millimeters;
pictureWatermark.Height = (ExportUtils.GetPageHeight(page) - page.TopMargin - page.BottomMargin) * Units.Millimeters;
pictureWatermark.SizeMode = PictureBoxSizeMode.Normal;
pictureWatermark.Image = new Bitmap((int)pictureWatermark.Width, (int)pictureWatermark.Height);
using (Graphics g = Graphics.FromImage(pictureWatermark.Image))
{
g.Clear(Color.Transparent);
if (drawText)
page.Watermark.DrawText(new FRPaintEventArgs(g, 1f, 1f, Report.GraphicCache),
new RectangleF(0, 0, pictureWatermark.Width, pictureWatermark.Height), Report, true);
else
page.Watermark.DrawImage(new FRPaintEventArgs(g, 1f, 1f, Report.GraphicCache),
new RectangleF(0, 0, pictureWatermark.Width, pictureWatermark.Height), Report, true);
pictureWatermark.Transparency = page.Watermark.ImageTransparency;
LayerBack(Page, pictureWatermark, null);
LayerPicture(Page, pictureWatermark, null);
}
}
}
private void ExportHTMLPageLayeredBegin(HTMLData d)
{
if (!singlePage && !WebMode)
cssStyles.Clear();
css.Clear();
htmlPage.Clear();
ReportPage reportPage = d.page;
if (reportPage != null)
{
maxWidth = ExportUtils.GetPageWidth(reportPage) * Units.Millimeters;
maxHeight = ExportUtils.GetPageHeight(reportPage) * Units.Millimeters;
if (enableMargins)
{
leftMargin = reportPage.LeftMargin * Units.Millimeters;
topMargin = reportPage.TopMargin * Units.Millimeters;
}
else
{
maxWidth -= (reportPage.LeftMargin + reportPage.RightMargin) * Units.Millimeters;
maxHeight -= (reportPage.TopMargin + reportPage.BottomMargin) * Units.Millimeters;
leftMargin = 0;
topMargin = 0;
}
currentPage = d.PageNumber - 1;
ExportHTMLPageStart(htmlPage, d.PageNumber, d.CurrentPage);
doPageBreak = singlePage && pageBreaks;
htmlPage.Append(HTMLGetAncor(d.PageNumber.ToString()));
if (doPageBreak && d.PageNumber > 1)
htmlPage.Append("");
pageStyleName = "frpage" + currentPage;
htmlPage.Append("");
if (reportPage.Watermark.Enabled && !reportPage.Watermark.ShowImageOnTop)
Watermark(htmlPage, reportPage, false);
if (reportPage.Watermark.Enabled && !reportPage.Watermark.ShowTextOnTop)
Watermark(htmlPage, reportPage, true);
}
}
private void ExportHTMLPageLayeredEnd(HTMLData d)
{
// to do
if (d.page != null && d.page.Watermark.Enabled)
{
if (d.page.Watermark.ShowImageOnTop)
Watermark(htmlPage, d.page, false);
if (d.page.Watermark.ShowTextOnTop)
Watermark(htmlPage, d.page, true);
}
ExportPageStylesLayers(css, d.PageNumber);
if (singlePage)
{
hPos = 0;
prevStyleListIndex = cssStyles.Count;
}
htmlPage.Append("
");
ExportHTMLPageFinal(css, htmlPage, d, maxWidth, maxHeight);
}
///
/// For developers only
///
[EditorBrowsable(EditorBrowsableState.Never)]
public void ExportReportObject(ReportComponentBase obj)
{
//Init();
var band = new ReportTitleBand();
obj.SetParent(band);
if (obj is ITranslatable translatableObject)
{
translatableObject.ConvertToReportObjects();
}
Init_WebMode();
//InlineStyles = true;
//SaveStreams = true;
EmbedPictures = true;
//Start();
count = 1;
StartWeb();
ExportBandLayers(band);
ProcessPageEnd(0, 0);
}
private void ExportObject(ReportComponentBase obj)
{
if (!String.IsNullOrEmpty(obj.Bookmark))
htmlPage.Append("