BlazorExportLayers.cs 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Text;
  5. using System.Collections.Generic;
  6. using FastReport.Table;
  7. using FastReport.Utils;
  8. using FastReport.Export;
  9. using FastReport.SVG;
  10. using System.Drawing.Imaging;
  11. using System.Windows.Forms;
  12. using Microsoft.AspNetCore.Components;
  13. using Microsoft.AspNetCore.Components.Rendering;
  14. using FastReport.Web.Blazor.Export.ReportComponent;
  15. using FastReport.AdvMatrix;
  16. using static FastReport.Web.Blazor.Export.BlazorConstants;
  17. using System.Diagnostics;
  18. using System.Drawing.Text;
  19. using System.Runtime.CompilerServices;
  20. namespace FastReport.Web.Blazor.Export
  21. {
  22. public class InputChanged
  23. {
  24. private readonly BlazorExport export;
  25. private readonly ReportComponentBase obj;
  26. private readonly int CurPage;
  27. public void Invoke()
  28. {
  29. export.OnClick(null, new OnClickEventArgs(obj, ClickType.TextEdit, CurPage));
  30. }
  31. public InputChanged(BlazorExport export, TextObject obj, int curPage)
  32. {
  33. this.export = export;
  34. this.obj = obj;
  35. this.CurPage = curPage;
  36. }
  37. }
  38. public partial class BlazorExport
  39. {
  40. private bool doPageBreak;
  41. const string defaultStyle = "position:absolute;";
  42. private StringBuilder GetStyle(Font? Font, in Color TextColor, in Color FillColor,
  43. bool RTL, HorzAlign HAlign, Border Border, bool WordWrap, float LineHeight, float Width, float Height, bool Clip)
  44. {
  45. StringBuilder style = new StringBuilder(256);
  46. if (Font != null)
  47. {
  48. if (Zoom != 1)
  49. {
  50. using (Font newFont = new Font(Font.FontFamily, Font.Size * Zoom, Font.Style, Font.Unit, Font.GdiCharSet, Font.GdiVerticalFont))
  51. HTMLFontStyle(style, newFont, LineHeight);
  52. }
  53. else
  54. HTMLFontStyle(style, Font, LineHeight);
  55. }
  56. style.Append("text-align:");
  57. if (HAlign == HorzAlign.Left)
  58. style.Append(RTL ? "right" : "left");
  59. else if (HAlign == HorzAlign.Right)
  60. style.Append(RTL ? "left" : "right");
  61. else if (HAlign == HorzAlign.Center)
  62. style.Append("center");
  63. else
  64. style.Append("justify");
  65. style.Append(';');
  66. if (WordWrap)
  67. style.Append("word-wrap:break-word;");
  68. if (Clip)
  69. style.Append("overflow:hidden;");
  70. style.Append("position:absolute;color:").
  71. Append(HTMLColor(TextColor)).
  72. Append(";background-color:").
  73. Append(FillColor.A == 0 ? "transparent" : HTMLColor(FillColor)).
  74. Append(';').Append(RTL ? "direction:rtl;" : String.Empty);
  75. Border newBorder = Border;
  76. HTMLBorder(style, newBorder);
  77. style.Append("width:").Append(Px(Math.Abs(Width) * Zoom)).Append("px;height:").Append(Px(Math.Abs(Height) * Zoom)).Append("px;");
  78. return style;
  79. }
  80. private int UpdateCSSTable(ReportComponentBase obj)
  81. {
  82. StringBuilder style;
  83. if (obj is TextObject textObj)
  84. {
  85. style = GetStyle(textObj.Font, textObj.TextColor, textObj.FillColor,
  86. textObj.RightToLeft, textObj.HorzAlign, textObj.Border, textObj.WordWrap, textObj.LineHeight,
  87. textObj.Width, textObj.Height, textObj.Clip);
  88. }
  89. else if (obj is HtmlObject htmlObj)
  90. {
  91. style = GetStyle(DrawUtils.DefaultTextObjectFont, Color.Black, htmlObj.FillColor,
  92. false, HorzAlign.Left, htmlObj.Border, true, 0, htmlObj.Width, htmlObj.Height, false);
  93. }
  94. else
  95. style = GetStyle(null, Color.White, obj.FillColor, false, HorzAlign.Center, obj.Border, false, 0, obj.Width, obj.Height, false);
  96. return UpdateCSSTable(style);
  97. }
  98. private int UpdateCSSTable(StringBuilder style)
  99. {
  100. int i = cssStyles.IndexOf(style);
  101. if (i == -1)
  102. {
  103. i = cssStyles.Count;
  104. cssStyles.Add(style);
  105. }
  106. return i;
  107. }
  108. private void ExportPageStylesLayers(IRenderContext context)
  109. {
  110. if (prevStyleListIndex < cssStyles.Count)
  111. {
  112. HTMLGetStylesHeader(context);
  113. context.OpenElement(STYLE);
  114. context.AddAttribute(TYPE, "text/css");
  115. var styles = new StringBuilder(256);
  116. styles.AppendLine("<!-- ");
  117. for (int i = prevStyleListIndex; i < cssStyles.Count; i++)
  118. styles.Append(HTMLGetStyleHeader(i))
  119. .Append(cssStyles[i])
  120. .Append('}')
  121. .AppendLine();
  122. styles.AppendLine("-->");
  123. context.AddContent(styles.ToString());
  124. context.CloseElement();
  125. }
  126. }
  127. private string GetStyleTag(int index)
  128. {
  129. return $"{stylePrefix}s{index}";
  130. }
  131. private void Layer(in IRenderContext context, ReportComponentBase obj,
  132. float left, float top, float width, float height, RenderFragment? content, string? styleClass, StringBuilder? addstyletag)
  133. {
  134. Debug.Assert(obj != null);
  135. var currentContext = new ElementContext(context);
  136. ClickType onclick = ClickType.Empty;
  137. if (!string.IsNullOrEmpty(obj.ClickEvent) || obj.HasClickListeners())
  138. {
  139. onclick = ClickType.Click;
  140. }
  141. if (obj is CheckBoxObject checkBoxObject && checkBoxObject.Editable)
  142. {
  143. onclick = ClickType.CheckboxClick;
  144. }
  145. if (obj is MatrixButton)
  146. {
  147. onclick = ClickType.AdvancedMatrixClick;
  148. }
  149. // we need to adjust left, top, width and height values because borders take up space in html elements
  150. float borderLeft;
  151. float borderRight;
  152. float borderTop;
  153. float borderBottom;
  154. HTMLBorderWidthValues(obj, out borderLeft, out borderTop, out borderRight, out borderBottom);
  155. bool needHref = !String.IsNullOrEmpty(obj.Hyperlink.Value);
  156. if (needHref)
  157. {
  158. GetHref(obj, currentContext); // return don't close <a>
  159. }
  160. currentContext.OpenElement(DIV);
  161. if (!string.IsNullOrEmpty(styleClass))
  162. {
  163. currentContext.AddAttribute(CLASS, styleClass);
  164. }
  165. StringBuilder sb = new StringBuilder(32);
  166. if (onclick != ClickType.Empty || needHref)
  167. sb.Append("cursor:pointer;");
  168. sb.Append("left:").Append(Px((leftMargin + left) * Zoom - borderLeft / 2f)).
  169. Append("px;top:").Append(Px((topMargin + top) * Zoom - borderTop / 2f)).
  170. Append("px;width:").Append(Px(width * Zoom - borderRight / 2f - borderLeft / 2f)).
  171. Append("px;height:").Append(Px(height * Zoom - borderBottom / 2f - borderTop / 2f)).
  172. Append("px;");
  173. sb.Append(addstyletag);
  174. currentContext.AddAttribute(STYLE, sb.ToString());
  175. if (obj is MatrixButton)
  176. {
  177. currentContext.AddOnClick(new OnClickEventArgs(obj, onclick, CurPage));
  178. }
  179. if (obj is TextObject textObject && textObject.Editable)
  180. {
  181. currentContext.AddContent(AddTextInput(textObject, styleClass));
  182. }
  183. else
  184. {
  185. if (onclick != ClickType.Empty)
  186. {
  187. currentContext.AddOnClick(new OnClickEventArgs(obj, onclick, CurPage));
  188. }
  189. if (content == null)
  190. currentContext.AddMarkupContent(NBSP);
  191. else
  192. currentContext.AddContent(content);
  193. }
  194. currentContext.Dispose();
  195. }
  196. private static void GetHref(ReportComponentBase obj, IRenderContext context)
  197. {
  198. context.OpenElement(A);
  199. if (obj is TextObject textObject)
  200. {
  201. string hrefStyle = "color:" + HTMLColor(textObject.TextColor);
  202. if (!textObject.Font.Underline)
  203. hrefStyle += ";text-decoration:none";
  204. context.AddAttribute(STYLE, hrefStyle);
  205. }
  206. var hyperlink = obj.Hyperlink;
  207. if (hyperlink.Kind == HyperlinkKind.URL)
  208. {
  209. context.AddAttribute(HREF, hyperlink.Value);
  210. if (hyperlink.OpenLinkInNewTab)
  211. context.AddAttribute("target", "_blank");
  212. }
  213. else if (hyperlink.Kind == HyperlinkKind.DetailReport)
  214. {
  215. context.AddOnClick(new OnClickEventArgs(obj, ClickType.DetailedReport));
  216. }
  217. else if (hyperlink.Kind == HyperlinkKind.DetailPage)
  218. {
  219. context.AddOnClick(new OnClickEventArgs(obj, ClickType.DetailedPage));
  220. }
  221. else if (hyperlink.Kind == HyperlinkKind.Bookmark)
  222. {
  223. context.AddOnClick(new OnClickEventArgs(obj, ClickType.Bookmark));
  224. }
  225. else if (hyperlink.Kind == HyperlinkKind.PageNumber)
  226. {
  227. context.AddOnClick(new OnClickEventArgs(obj, ClickType.PageNumber));
  228. }
  229. //context.CloseElement();
  230. }
  231. private RenderFragment AddTextInput(TextObject obj, string classTag)
  232. {
  233. return b =>
  234. {
  235. var context = new RenderContext(b);
  236. context.OpenComponent<TextInput>();
  237. context.AddAttribute(nameof(TextInput.TextObject), obj);
  238. context.AddAttribute(nameof(TextInput.ClassTag), classTag);
  239. context.AddAttribute(nameof(TextInput.OnChange), new InputChanged(this, obj, CurPage));
  240. context.CloseComponent();
  241. };
  242. }
  243. private RenderFragment GetSpanText(TextObjectBase obj, RenderFragment text,
  244. float top, float width,
  245. float ParagraphOffset)
  246. {
  247. return builder =>
  248. {
  249. var context = new RenderContext(builder);
  250. StringBuilder style = new StringBuilder(64);
  251. style.Append("display:block;border:0;white-space: pre-wrap;width:").Append(PxClosed(width * Zoom));
  252. if (ParagraphOffset != 0)
  253. style.Append("text-indent:").Append(PxClosed(ParagraphOffset * Zoom));
  254. if (obj.Padding.Left != 0)
  255. style.Append("padding-left:").Append(PxClosed(obj.Padding.Left * Zoom));
  256. if (obj.Padding.Right != 0)
  257. style.Append("padding-right:").Append(PxClosed(obj.Padding.Right * Zoom));
  258. if (top != 0)
  259. style.Append("margin-top:").Append(PxClosed(top * Zoom));
  260. if (obj is TextObject { WordWrap: false })
  261. style.Append("overflow: hidden; text-wrap: nowrap;");
  262. // we need to apply border width in order to position our div perfectly
  263. float borderLeft;
  264. float borderTop;
  265. if (HTMLBorderWidthValues(obj, out borderLeft, out borderTop, out _, out _))
  266. {
  267. style.Append("position:absolute;left:").Append(Px(-1 * borderLeft / 2f))
  268. .Append("px;top:").Append(Px(-1 * borderTop / 2f)).Append("px;");
  269. }
  270. context.OpenElement(DIV);
  271. context.AddAttribute(CLASS, GetStyleTag(UpdateCSSTable(style)));
  272. context.AddContent(text);
  273. context.CloseElement();
  274. };
  275. }
  276. private void LayerText(IRenderContext context, TextObject obj)
  277. {
  278. float top = 0;
  279. RenderFragment text;
  280. switch (obj.TextRenderType)
  281. {
  282. case TextRenderType.HtmlParagraph:
  283. RenderFragment htmlParagraph;
  284. using (HtmlTextRenderer htmlTextRenderer = obj.GetHtmlTextRenderer(Zoom, Zoom))
  285. {
  286. if (obj.VertAlign == VertAlign.Center)
  287. {
  288. top = (obj.Height - htmlTextRenderer.CalcHeight()) / 2;
  289. }
  290. else if (obj.VertAlign == VertAlign.Bottom)
  291. {
  292. top = obj.Height - htmlTextRenderer.CalcHeight();
  293. }
  294. htmlParagraph = GetHtmlParagraph(htmlTextRenderer);
  295. }
  296. text = GetSpanText(obj, htmlParagraph,
  297. top + obj.Padding.Top,
  298. obj.Width - obj.Padding.Horizontal,
  299. obj.ParagraphOffset);
  300. break;
  301. default:
  302. if (obj.VertAlign != VertAlign.Top)
  303. {
  304. IGraphics g = htmlMeasureGraphics;
  305. using (Font f = new Font(obj.Font.FontFamily, obj.Font.Size * DrawUtils.ScreenDpiFX, obj.Font.Style))
  306. {
  307. RectangleF textRect = new RectangleF(obj.AbsLeft + obj.Padding.Left, obj.AbsTop + obj.Padding.Top,
  308. obj.Width - obj.Padding.Left - obj.Padding.Right,
  309. obj.Height - obj.Padding.Top - obj.Padding.Bottom);
  310. StringFormat format = obj.GetStringFormat(Report.GraphicCache, 0);
  311. Brush textBrush = Report.GraphicCache.GetBrush(obj.TextColor);
  312. AdvancedTextRenderer renderer = new AdvancedTextRenderer(obj.Text, g, f, textBrush, null,
  313. textRect, format, obj.HorzAlign, obj.VertAlign, obj.LineHeight, obj.Angle, obj.FontWidthRatio,
  314. obj.ForceJustify, obj.Wysiwyg, obj.HasHtmlTags, false, Zoom, Zoom, obj.InlineImageCache);
  315. if (renderer.Paragraphs.Count > 0)
  316. {
  317. if (renderer.Paragraphs[0].Lines.Count > 0)
  318. {
  319. float height = renderer.Paragraphs[0].Lines[0].CalcHeight();
  320. if (height > obj.Height)
  321. top = -(height - obj.Height) / 2;
  322. else
  323. {
  324. top = renderer.Paragraphs[0].Lines[0].Top - obj.AbsTop;
  325. height = renderer.CalcHeight();
  326. if (obj.VertAlign == VertAlign.Center)
  327. {
  328. top = (obj.Height - height - obj.Padding.Bottom + obj.Padding.Top) / 2;
  329. if (top < 0)
  330. {
  331. if (obj.Height > height)
  332. top = (obj.Height - height - obj.Padding.Bottom + obj.Padding.Top) / 2;
  333. else
  334. top = (height - obj.Height - obj.Padding.Bottom + obj.Padding.Top) / 2;
  335. }
  336. }
  337. else if (obj.VertAlign == VertAlign.Bottom)
  338. {
  339. // (float)(Math.Round(obj.Font.Size * 96 / 72) / 2
  340. // necessary to compensate for paragraph offset error in GetSpanText method below
  341. top = obj.Height - height - obj.Padding.Bottom - (float)(Math.Round(obj.Font.Size * 96 / 72) / 2);
  342. }
  343. }
  344. }
  345. }
  346. }
  347. }
  348. void htmlStringParse(RenderTreeBuilder builder)
  349. {
  350. var content = ExportUtils.HtmlString(obj.Text, obj.TextRenderType).ToString();
  351. var context = new RenderContext(builder);
  352. context.AddMarkupContent(content);
  353. }
  354. text = GetSpanText(obj, htmlStringParse,
  355. top + obj.Padding.Top,
  356. obj.Width - obj.Padding.Horizontal,
  357. obj.ParagraphOffset);
  358. break;
  359. }
  360. context.AddContent(LayerBack(obj, text));
  361. }
  362. private static RenderFragment GetHtmlParagraph(HtmlTextRenderer renderer)
  363. {
  364. return builder =>
  365. {
  366. var context = new RenderContext(builder);
  367. foreach (HtmlTextRenderer.Paragraph paragraph in renderer.Paragraphs)
  368. foreach (HtmlTextRenderer.Line line in paragraph.Lines)
  369. {
  370. StringBuilder sb = new StringBuilder(128);
  371. context.OpenElement(SPAN);
  372. sb.Append("display:block;");
  373. if (line.Top + line.Height > renderer.DisplayRect.Bottom)
  374. sb.Append("height:").Append(Math.Max(renderer.DisplayRect.Bottom - line.Top, 0).ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
  375. else
  376. {
  377. //sb.Append("height:").Append(line.Height.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
  378. if (line.LineSpacing > 0)
  379. {
  380. sb.Append("margin-bottom:").Append(line.LineSpacing.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
  381. }
  382. }
  383. sb.Append("overflow:hidden;");
  384. sb.Append("line-height:").Append(line.Height.ToString(HtmlTextRenderer.CultureInfo)).Append("px;");
  385. if (line.HorzAlign == HorzAlign.Justify)
  386. sb.Append("text-align-last:justify;");
  387. else sb.Append("white-space:pre;");
  388. context.AddAttribute(STYLE, sb.ToString());
  389. FastString htmlText = new FastString();
  390. HtmlTextRenderer.StyleDescriptor? styleDesc = null;
  391. foreach (HtmlTextRenderer.Word word in line.Words)
  392. {
  393. foreach (HtmlTextRenderer.Run run in word.Runs)
  394. {
  395. if (!run.Style.FullEquals(styleDesc))
  396. {
  397. if (styleDesc != null)
  398. styleDesc.ToHtml(htmlText, true); // TODO: close ???
  399. styleDesc = run.Style;
  400. styleDesc.ToHtml(htmlText, false);
  401. }
  402. if (run is HtmlTextRenderer.RunText runText)
  403. {
  404. foreach (char ch in runText.Text)
  405. {
  406. switch (ch)
  407. {
  408. case '"':
  409. htmlText.Append("&quot;");
  410. break;
  411. case '&':
  412. htmlText.Append("&amp;");
  413. break;
  414. case '<':
  415. htmlText.Append("&lt;");
  416. break;
  417. case '>':
  418. htmlText.Append("&gt;");
  419. break;
  420. case '\t':
  421. htmlText.Append("&Tab;");
  422. break;
  423. default:
  424. htmlText.Append(ch);
  425. break;
  426. }
  427. }
  428. }
  429. else if (run is HtmlTextRenderer.RunImage runImage)
  430. {
  431. using (MemoryStream ms = new MemoryStream())
  432. {
  433. try
  434. {
  435. float w, h;
  436. using (Bitmap bmp = runImage.GetBitmap(out w, out h))
  437. {
  438. bmp.Save(ms, ImageFormat.Png);
  439. }
  440. ms.Flush();
  441. htmlText.Append("<img src=\"data:image/png;base64,")
  442. .Append(Convert.ToBase64String(ms.ToArray()))
  443. .Append("\" width=\"").Append(w.ToString(HtmlTextRenderer.CultureInfo))
  444. .Append("\" height=\"").Append(h.ToString(HtmlTextRenderer.CultureInfo))
  445. .Append("\"/>");
  446. }
  447. catch (Exception /*e*/)
  448. {
  449. }
  450. }
  451. }
  452. //run.ToHtml(htmlText, true);
  453. }
  454. }
  455. if (styleDesc != null)
  456. styleDesc.ToHtml(htmlText, true);
  457. else htmlText.Append("<br/>");
  458. Debug.WriteLine($"BlazorExportLayers[485] SB.Length: {htmlText.Length}");
  459. context.AddMarkupContent(htmlText.ToString());
  460. context.CloseElement();
  461. }
  462. };
  463. }
  464. private RenderFragment LayerHtml(HtmlObject obj)
  465. {
  466. void textFragment(RenderTreeBuilder builder)
  467. {
  468. var context = new RenderContext(builder);
  469. context.AddContent(obj.Text);
  470. }
  471. return LayerBack(obj,
  472. GetSpanText(obj, textFragment,
  473. obj.Padding.Top,
  474. obj.Width - obj.Padding.Horizontal,
  475. 0));
  476. }
  477. private string? GetLayerPicture(ReportComponentBase obj, out float Width, out float Height)
  478. {
  479. if(!pictures)
  480. {
  481. Width = 0;
  482. Height = 0;
  483. return null;
  484. }
  485. MemoryStream PictureStream = new MemoryStream();
  486. Width = obj.Width == 0 ? obj.Border.LeftLine.Width : obj.Width;
  487. Height = obj.Height == 0 ? obj.Border.TopLine.Width : obj.Height;
  488. if (Math.Abs(Width) * Zoom < 1 && Zoom > 0)
  489. Width = 1 / Zoom;
  490. if (Math.Abs(Height) * Zoom < 1 && Zoom > 0)
  491. Height = 1 / Zoom;
  492. using (Image image =
  493. new Bitmap(
  494. (int)Math.Abs(Math.Round(Width * Zoom)),
  495. (int)Math.Abs(Math.Round(Height * Zoom)))
  496. )
  497. {
  498. using (Graphics g = Graphics.FromImage(image))
  499. {
  500. var needClear = obj is TextObjectBase
  501. #if MSCHART
  502. or MSChart.MSChartObject
  503. #endif
  504. or Gauge.GaugeObject;
  505. if (needClear)
  506. {
  507. g.Clear(Color.Transparent);
  508. g.TextRenderingHint = TextRenderingHint.AntiAlias;
  509. }
  510. float Left = Width > 0 ? obj.AbsLeft : obj.AbsLeft + Width;
  511. float Top = Height > 0 ? obj.AbsTop : obj.AbsTop + Height;
  512. float dx = 0;
  513. float dy = 0;
  514. g.TranslateTransform((-Left - dx) * Zoom, (-Top - dy) * Zoom);
  515. BorderLines oldLines = obj.Border.Lines;
  516. obj.Border.Lines = BorderLines.None;
  517. obj.Draw(new FRPaintEventArgs(g, Zoom, Zoom, Report.GraphicCache));
  518. obj.Border.Lines = oldLines;
  519. }
  520. ImageFormat FPictureFormat = GetImageFormat();
  521. if (FPictureFormat == ImageFormat.Jpeg)
  522. ExportUtils.SaveJpeg(image, PictureStream, 95);
  523. else
  524. image.Save(PictureStream, FPictureFormat);
  525. }
  526. PictureStream.Position = 0;
  527. string hash = String.Empty;
  528. if (obj is PictureObject pic)
  529. {
  530. if (pic.Image != null)
  531. {
  532. hash = Crypter.ComputeHash(PictureStream);
  533. PictureStream.Position = 0;
  534. }
  535. }
  536. else
  537. hash = Crypter.ComputeHash(PictureStream);
  538. string result = HTMLGetImage(d.CurrentPage, hash, PictureStream, false);
  539. return result;
  540. }
  541. private ImageFormat GetImageFormat()
  542. {
  543. ImageFormat FPictureFormat = ImageFormat.Bmp;
  544. if (imageFormat == ImageFormat.Png)
  545. FPictureFormat = ImageFormat.Png;
  546. else if (imageFormat == ImageFormat.Jpeg)
  547. FPictureFormat = ImageFormat.Jpeg;
  548. else if (imageFormat == ImageFormat.Gif)
  549. FPictureFormat = ImageFormat.Gif;
  550. return FPictureFormat;
  551. }
  552. #if !WASM
  553. private void LayerPicture(IRenderContext context, ReportComponentBase obj, RenderFragment? text)
  554. {
  555. if (pictures)
  556. {
  557. int styleindex = UpdateCSSTable(obj);
  558. string old_text = String.Empty;
  559. if (IsMemo(obj))
  560. {
  561. old_text = (obj as TextObject).Text;
  562. (obj as TextObject).Text = String.Empty;
  563. }
  564. float Width, Height;
  565. string? pic = GetLayerPicture(obj, out Width, out Height);
  566. if (IsMemo(obj))
  567. (obj as TextObject).Text = old_text;
  568. StringBuilder picStyleBuilder = new StringBuilder("background: url('")
  569. .Append(pic).Append("') no-repeat !important;-webkit-print-color-adjust:exact;");
  570. int picStyleIndex = UpdateCSSTable(picStyleBuilder);
  571. string style = $"{stylePrefix}s{styleindex} {stylePrefix}s{picStyleIndex}";
  572. //FastString addstyle = new FastString(128);
  573. //addstyle.Append(" background: url('").Append(pic).Append("') no-repeat !important;-webkit-print-color-adjust:exact;");
  574. //if (String.IsNullOrEmpty(text))
  575. // text = NBSP;
  576. float x = Width > 0 ? obj.AbsLeft : (obj.AbsLeft + Width);
  577. float y = Height > 0 ? obj.AbsTop : (obj.AbsTop + Height);
  578. Layer(context, obj, x, y, Width, Height, text, style, null);
  579. }
  580. }
  581. #endif
  582. private void LayerShape(IRenderContext context, ShapeObject obj)
  583. {
  584. StringBuilder addstyle = new StringBuilder(64);
  585. addstyle.Append(defaultStyle);
  586. addstyle.Append("background: url('").
  587. Append(GetLayerPicture(obj, out _, out _)).
  588. Append("');no-repeat !important;-webkit-print-color-adjust:exact;");
  589. float x = obj.Width > 0 ? obj.AbsLeft : (obj.AbsLeft + obj.Width);
  590. float y = obj.Height > 0 ? obj.AbsTop : (obj.AbsTop + obj.Height);
  591. Layer(context, obj, x, y, obj.Width, obj.Height, null, null, addstyle);
  592. }
  593. private RenderFragment LayerBack(ReportComponentBase obj, RenderFragment? text)
  594. {
  595. return (builder) =>
  596. {
  597. var context = new RenderContext(builder, OnClick);
  598. if (obj.Border.Shadow)
  599. {
  600. using (TextObject shadow = new TextObject())
  601. {
  602. shadow.Left = obj.AbsLeft + obj.Border.ShadowWidth + obj.Border.LeftLine.Width;
  603. shadow.Top = obj.AbsTop + obj.Height + obj.Border.BottomLine.Width;
  604. shadow.Width = obj.Width + obj.Border.RightLine.Width;
  605. shadow.Height = obj.Border.ShadowWidth + obj.Border.BottomLine.Width;
  606. shadow.FillColor = obj.Border.ShadowColor;
  607. shadow.Border.Lines = BorderLines.None;
  608. context.AddContent(LayerBack(shadow, null));
  609. shadow.Left = obj.AbsLeft + obj.Width + obj.Border.RightLine.Width;
  610. shadow.Top = obj.AbsTop + obj.Border.ShadowWidth + obj.Border.TopLine.Width;
  611. shadow.Width = obj.Border.ShadowWidth + obj.Border.RightLine.Width;
  612. shadow.Height = obj.Height;
  613. context.AddContent(LayerBack(shadow, null));
  614. }
  615. }
  616. if (!(obj is PolyLineObject))
  617. {
  618. if (obj.Fill is SolidFill)
  619. Layer(context, obj, obj.AbsLeft, obj.AbsTop, obj.Width, obj.Height, text, GetStyleTag(UpdateCSSTable(obj)), null);
  620. else
  621. LayerPicture(context, obj, text);
  622. }
  623. };
  624. }
  625. private void LayerTable(IRenderContext context, TableBase table)
  626. {
  627. float y = 0;
  628. for (int i = 0; i < table.RowCount; i++)
  629. {
  630. float x = 0;
  631. for (int j = 0; j < table.ColumnCount; j++)
  632. {
  633. if (!table.IsInsideSpan(table[j, i]))
  634. {
  635. TableCell textcell = table[j, i];
  636. textcell.Left = x;
  637. textcell.Top = y;
  638. if (textcell is TextObject && !textcell.TextOutline.Enabled && IsMemo(textcell))
  639. LayerText(context, textcell);
  640. else
  641. {
  642. context.AddContent(LayerBack(textcell, null));
  643. LayerPicture(context, textcell, null);
  644. }
  645. }
  646. x += table.Columns[j].Width;
  647. }
  648. y += table.Rows[i].Height;
  649. }
  650. }
  651. private static bool IsMemo(ReportComponentBase Obj)
  652. {
  653. if(Obj is TextObject aObj)
  654. {
  655. return (aObj.Angle == 0) && (aObj.FontWidthRatio == 1) && (!aObj.TextOutline.Enabled) && (!aObj.Underlines);
  656. }
  657. return false;
  658. }
  659. private void Watermark(IRenderContext context, ReportPage page, bool drawText)
  660. {
  661. using (PictureObject pictureWatermark = new PictureObject())
  662. {
  663. pictureWatermark.Left = 0;
  664. pictureWatermark.Top = 0;
  665. pictureWatermark.Width = (ExportUtils.GetPageWidth(page) - page.LeftMargin - page.RightMargin) * Units.Millimeters;
  666. pictureWatermark.Height = (ExportUtils.GetPageHeight(page) - page.TopMargin - page.BottomMargin) * Units.Millimeters;
  667. pictureWatermark.SizeMode = PictureBoxSizeMode.Normal;
  668. pictureWatermark.Image = new Bitmap((int)pictureWatermark.Width, (int)pictureWatermark.Height);
  669. using (Graphics g = Graphics.FromImage(pictureWatermark.Image))
  670. {
  671. g.Clear(Color.Transparent);
  672. if (drawText)
  673. page.Watermark.DrawText(new FRPaintEventArgs(g, 1f, 1f, Report.GraphicCache),
  674. new RectangleF(0, 0, pictureWatermark.Width, pictureWatermark.Height), Report, true);
  675. else
  676. page.Watermark.DrawImage(new FRPaintEventArgs(g, 1f, 1f, Report.GraphicCache),
  677. new RectangleF(0, 0, pictureWatermark.Width, pictureWatermark.Height), Report, true);
  678. pictureWatermark.Transparency = page.Watermark.ImageTransparency;
  679. context.AddContent(LayerBack(pictureWatermark, null));
  680. LayerPicture(context, pictureWatermark, null);
  681. }
  682. }
  683. }
  684. private void ExportHTMLPageLayeredBegin(in HTMLData d)
  685. {
  686. //bands = new List<RenderFragment>();
  687. ReportPage reportPage = d.page;
  688. if (reportPage != null)
  689. {
  690. maxWidth = ExportUtils.GetPageWidth(reportPage) * Units.Millimeters;
  691. maxHeight = ExportUtils.GetPageHeight(reportPage) * Units.Millimeters;
  692. if (enableMargins)
  693. {
  694. leftMargin = reportPage.LeftMargin * Units.Millimeters;
  695. topMargin = reportPage.TopMargin * Units.Millimeters;
  696. }
  697. else
  698. {
  699. maxWidth -= (reportPage.LeftMargin + reportPage.RightMargin) * Units.Millimeters;
  700. maxHeight -= (reportPage.TopMargin + reportPage.BottomMargin) * Units.Millimeters;
  701. leftMargin = 0;
  702. topMargin = 0;
  703. }
  704. currentPage = d.PageNumber - 1;
  705. ExportHTMLPageStart(d.PageNumber, d.CurrentPage);
  706. doPageBreak = singlePage && pageBreaks;
  707. pageStyleName = "frpage" + currentPage;
  708. BeginLayer(d);
  709. }
  710. }
  711. void BeginLayer(in HTMLData d)
  712. {
  713. ReportPage reportPage = d.page;
  714. var context = globalContext;
  715. context.AddContent(HTMLGetAncor(d.PageNumber));
  716. context.OpenElement(DIV);
  717. if (doPageBreak)
  718. context.AddAttribute(CLASS, pageStyleName);
  719. string style = $"position:relative; width:{Px(maxWidth * Zoom + 3)}px; height:{Px(maxHeight * Zoom)}px;";
  720. if (reportPage.Fill is SolidFill fill)
  721. {
  722. style += " background-color:" + (fill.IsTransparent ? "transparent" : HTMLColor(fill.Color));
  723. }
  724. else
  725. {
  726. // to-do for picture background
  727. }
  728. context.AddAttribute(STYLE, style);
  729. if(reportPage.Watermark.Enabled)
  730. {
  731. if (!reportPage.Watermark.ShowImageOnTop)
  732. Watermark(context, reportPage, false);
  733. if (!reportPage.Watermark.ShowTextOnTop)
  734. Watermark(context, reportPage, true);
  735. }
  736. }
  737. void EndLayer(IRenderContext context, in HTMLData d)
  738. {
  739. // to do
  740. ReportPage reportPage = d.page;
  741. if(reportPage.Watermark.Enabled)
  742. {
  743. if (reportPage.Watermark.ShowImageOnTop)
  744. Watermark(context, reportPage, false);
  745. if (reportPage.Watermark.ShowTextOnTop)
  746. Watermark(context, reportPage, true);
  747. }
  748. context.CloseElement(); // "</div>"
  749. }
  750. private void ExportHTMLPageLayeredEnd(in HTMLData d)
  751. {
  752. var context = globalContext;
  753. EndLayer(context, d);
  754. ExportPageStylesLayers(context);
  755. if (singlePage)
  756. {
  757. prevStyleListIndex = cssStyles.Count;
  758. }
  759. ExportHTMLPageFinal(d, maxWidth, maxHeight);
  760. }
  761. private void ExportBandLayers(in BandBase band)
  762. {
  763. var context = globalContext;
  764. context.AddContent(LayerBack(band, null));
  765. foreach (Base c in band.ForEachAllConvectedObjects(this))
  766. {
  767. ExportObject(c as ReportComponentBase, context);
  768. }
  769. }
  770. private void ExportObject(ReportComponentBase? obj, in IRenderContext context)
  771. {
  772. if (obj == null)
  773. return;
  774. if (cancellationToken.IsCancellationRequested)
  775. return;
  776. if (obj is MatrixButton matrixButton)
  777. {
  778. matrixButton.Exportable = true;
  779. }
  780. if (obj.Exportable)
  781. {
  782. if (!String.IsNullOrEmpty(obj.Bookmark))
  783. {
  784. context.OpenElement(A);
  785. context.AddAttribute(NAME, obj.Bookmark);
  786. context.CloseElement();
  787. }
  788. if (obj is CellularTextObject cellularTextObject)
  789. obj = cellularTextObject.GetTable();
  790. if (obj is TableCell)
  791. return;
  792. else if (obj is TableBase table)
  793. {
  794. if (table.ColumnCount > 0 && table.RowCount > 0)
  795. {
  796. using (TextObject tableback = new TextObject())
  797. {
  798. tableback.Border = table.Border;
  799. tableback.Fill = table.Fill;
  800. tableback.FillColor = table.FillColor;
  801. tableback.Left = table.AbsLeft;
  802. tableback.Top = table.AbsTop;
  803. float tableWidth = 0;
  804. float tableHeight = 0;
  805. for (int i = 0; i < table.ColumnCount; i++)
  806. tableWidth += table[i, 0].Width;
  807. for (int i = 0; i < table.RowCount; i++)
  808. tableHeight += table.Rows[i].Height;
  809. tableback.Width = (tableWidth < table.Width) ? tableWidth : table.Width;
  810. tableback.Height = tableHeight;
  811. LayerText(context, tableback);
  812. }
  813. LayerTable(context, table);
  814. }
  815. }
  816. else if (IsMemo(obj))
  817. {
  818. LayerText(context, obj as TextObject);
  819. }
  820. else if (obj is HtmlObject htmlObject)
  821. {
  822. context.AddContent(LayerHtml(htmlObject));
  823. }
  824. else if (obj is BandBase)
  825. {
  826. context.AddContent(LayerBack(obj, null));
  827. }
  828. else if (obj is LineObject)
  829. {
  830. LayerPicture(context, obj, null);
  831. }
  832. else if (obj is ShapeObject shapeObject)
  833. {
  834. LayerShape(context, shapeObject);
  835. }
  836. else if (HasExtendedExport(obj))
  837. {
  838. LayerSVG(context, obj as SVGObject);
  839. }
  840. else
  841. {
  842. context.AddContent(LayerBack(obj, null));
  843. LayerPicture(context, obj, null);
  844. }
  845. }
  846. }
  847. }
  848. }