Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
XmlLayout.cs
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Text;
5using System.Threading.Tasks;
6using System.Xml;
7using SkiaSharp;
18using Waher.Events;
22using Waher.Script;
24using Waher.Security;
25
27{
28 internal class GraphInfo
29 {
30 public string FileName;
31 public string Title;
32 public bool Dynamic;
33 public byte[] DynamicContent;
34 }
35
41 {
42 internal const string DefaultContentType = ImageCodec.ContentTypePng;
43 internal const string DefaultFileExtension = ImageCodec.FileExtensionPng;
44 internal const SKEncodedImageFormat DefaultFormat = SKEncodedImageFormat.Png;
45 internal const int DefaultQuality = 100;
46
47 private static readonly Random rnd = new Random();
48 private static Scheduler scheduler = null;
49 private static string layoutFolder = null;
50 private static string contentRootFolder = null;
51
55 public XmlLayout()
56 {
57 }
58
64 public static void Init(string ContentRootFolder)
65 {
66 contentRootFolder = ContentRootFolder;
67 layoutFolder = Path.Combine(contentRootFolder, "Layout");
68
69 if (scheduler is null)
70 {
71 if (Types.TryGetModuleParameter("Scheduler", out object Obj) && Obj is Scheduler Scheduler)
72 scheduler = Scheduler;
73 else
74 {
75 scheduler = new Scheduler();
76
77 Log.Terminating += (Sender, e) =>
78 {
79 scheduler?.Dispose();
80 scheduler = null;
81 };
82 }
83 }
84
85 if (!Directory.Exists(layoutFolder))
86 Directory.CreateDirectory(layoutFolder);
87
88 DeleteOldFiles(TimeSpan.FromDays(7));
89 }
90
91 private static void DeleteOldFiles(object P)
92 {
93 if (P is TimeSpan MaxAge)
94 DeleteOldFiles(MaxAge, true);
95 }
96
102 public static void DeleteOldFiles(TimeSpan MaxAge, bool Reschedule)
103 {
104 if (string.IsNullOrEmpty(layoutFolder))
105 return;
106
107 DateTime Limit = DateTime.Now - MaxAge;
108 int Count = 0;
109
110 DirectoryInfo LayoutFolder = new DirectoryInfo(layoutFolder);
111 FileInfo[] Files = LayoutFolder.GetFiles("*.*");
112
113 foreach (FileInfo FileInfo in Files)
114 {
115 if (FileInfo.LastAccessTime < Limit)
116 {
117 try
118 {
119 File.Delete(FileInfo.FullName);
120 Count++;
121 }
122 catch (Exception ex)
123 {
124 Log.Error("Unable to delete old file: " + ex.Message, FileInfo.FullName);
125 }
126 }
127 }
128
129 if (Count > 0)
130 Log.Informational(Count.ToString() + " old file(s) deleted.", layoutFolder);
131
132 if (Reschedule)
133 {
134 lock (rnd)
135 {
136 scheduler.Add(DateTime.Now.AddDays(rnd.NextDouble() * 2), DeleteOldFiles, MaxAge);
137 }
138 }
139 }
140
146 public Grade Supports(string Language)
147 {
148 int i = Language.IndexOf(':');
149 if (i > 0)
150 Language = Language.Substring(0, i).TrimEnd();
151
152 switch (Language.ToLower())
153 {
154 case "layout":
155 return Grade.Excellent;
156 }
157
158 return Grade.NotAtAll;
159 }
160
164 public bool EvaluatesScript => true;
165
170 public void Register(MarkdownDocument Document)
171 {
172 // Do nothing.
173 }
174
184 public async Task<bool> RenderHtml(HtmlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
185 {
186 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
187 if (Info?.FileName is null)
188 return false;
189
190 string FileName = Info.FileName.Substring(contentRootFolder.Length).Replace(Path.DirectorySeparatorChar, '/');
191 if (!FileName.StartsWith("/"))
192 FileName = "/" + FileName;
193
194 StringBuilder Output = Renderer.Output;
195
196 Output.Append("<figure>");
197 Output.Append("<img src=\"");
198 if (Info.Dynamic)
199 Output.Append(ImageContent.GenerateUrl(Info.DynamicContent, DefaultContentType));
200 else
201 Output.Append(XML.HtmlAttributeEncode(FileName));
202
203 if (!string.IsNullOrEmpty(Info.Title))
204 {
205 Output.Append("\" alt=\"");
206 Output.Append(XML.HtmlAttributeEncode(Info.Title));
207
208 Output.Append("\" title=\"");
209 Output.Append(XML.HtmlAttributeEncode(Info.Title));
210 }
211 else
212 Output.Append("\" alt=\"2D-layout");
213
214 Output.Append("\" class=\"aloneUnsized\"/>");
215
216 if (!string.IsNullOrEmpty(Info.Title))
217 {
218 Output.Append("<figcaption>");
219 Output.Append(XML.HtmlValueEncode(Info.Title));
220 Output.Append("</figcaption>");
221 }
222
223 Output.AppendLine("</figure>");
224
225 return true;
226 }
227
238 internal static Task<GraphInfo> GetFileName(string Language, string[] Rows, Variables Session,
239 SKEncodedImageFormat ImageFormat, int Quality, string FileExtension)
240 {
241 return GetFileName(Language, MarkdownDocument.AppendRows(Rows), Session, ImageFormat, Quality, FileExtension);
242 }
243
254 internal static async Task<GraphInfo> GetFileName(string Language, string Xml, Variables Session,
255 SKEncodedImageFormat ImageFormat, int Quality, string FileExtension)
256 {
257 GraphInfo Result = new GraphInfo();
258 int i = Language.IndexOf(':');
259
260 if (i > 0)
261 {
262 Result.Title = Language.Substring(i + 1).Trim();
263 Language = Language.Substring(0, i).TrimEnd();
264 }
265 else
266 Result.Title = string.Empty;
267
268 string GraphBgColor = GetColor(Graph.GraphBgColorVariableName, Session);
269 string GraphFgColor = GetColor(Graph.GraphFgColorVariableName, Session);
270
271 string Hash = Hashes.ComputeSHA256HashString(Encoding.UTF8.GetBytes(Xml + Language + GraphBgColor + GraphFgColor));
272
273 string LayoutFolder = Path.Combine(contentRootFolder, "Layout");
274 string FileName = Path.Combine(LayoutFolder, Hash);
275 Result.FileName = FileName + "." + FileExtension;
276
277 if (!File.Exists(Result.FileName))
278 {
279 XmlDocument Doc = new XmlDocument();
280 Doc.LoadXml(Xml);
281
282 Layout2DDocument LayoutDoc = await Layout2DDocument.FromXml(Doc, Session);
283 RenderSettings Settings = await LayoutDoc.GetRenderSettings(Session);
284
285 KeyValuePair<SKImage, Map[]> P = await LayoutDoc.Render(Settings);
286 using (SKImage Img = P.Key) // TODO: Maps
287 {
288 using (SKData Data = Img.Encode(ImageFormat, Quality))
289 {
290 Result.DynamicContent = Data.ToArray();
291 Result.Dynamic = LayoutDoc.Dynamic;
292
293 if (!LayoutDoc.Dynamic)
294 await Resources.WriteAllBytesAsync(Result.FileName, Result.DynamicContent);
295 }
296 }
297 }
298
299 return Result;
300 }
301
302 private static string GetColor(string VariableName, Variables Variables)
303 {
304 if (Variables is null)
305 return null;
306
307 if (!Variables.TryGetVariable(VariableName, out Variable v))
308 return null;
309
310 if (v.ValueObject is SKColor Color)
311 return Graph.ToRGBAStyle(Color);
312 else if (v.ValueObject is string s)
313 return s;
314 else
315 return null;
316 }
317
327 public async Task<bool> RenderMarkdown(MarkdownRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
328 {
329 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
330 if (Info?.FileName is null)
331 return false;
332
333 if (Info.Dynamic)
334 {
335 ImageContent.GenerateMarkdown(Renderer.Output, Info.DynamicContent, DefaultContentType, Info.Title);
336 return true;
337 }
338 else
339 return await ImageContent.GenerateMarkdownFromFile(Renderer.Output, Info.FileName, Info.Title);
340 }
341
351 public async Task<bool> RenderText(TextRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
352 {
353 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
354 Renderer.Output.AppendLine(Info.Title);
355
356 return true;
357 }
358
368 public async Task<bool> RenderWpfXaml(WpfXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
369 {
370 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
371 if (Info?.FileName is null)
372 return false;
373
374 XmlWriter Output = Renderer.XmlOutput;
375
376 if (Info.Dynamic)
377 {
378 await Wpf.Multimedia.ImageContent.OutputWpf(Output, new ImageSource()
379 {
380 Url = ImageContent.GenerateUrl(Info.DynamicContent, DefaultContentType)
381 }, Info.Title);
382 }
383 else
384 {
385 Output.WriteStartElement("Image");
386 Output.WriteAttributeString("Source", Info.FileName);
387 Output.WriteAttributeString("Stretch", "None");
388
389 if (!string.IsNullOrEmpty(Info.Title))
390 Output.WriteAttributeString("ToolTip", Info.Title);
391
392 Output.WriteEndElement();
393 }
394
395 return true;
396 }
397
407 public async Task<bool> RenderXamarinFormsXaml(XamarinFormsXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
408 {
409 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
410 if (Info?.FileName is null)
411 return false;
412
413 XmlWriter Output = Renderer.XmlOutput;
414
415 if (Info.Dynamic)
416 {
417 await Xamarin.Multimedia.ImageContent.OutputXamarinForms(Output, new ImageSource()
418 {
419 Url = ImageContent.GenerateUrl(Info.DynamicContent, DefaultContentType)
420 });
421 }
422 else
423 {
424 Output.WriteStartElement("Image");
425 Output.WriteAttributeString("Source", Info.FileName);
426 Output.WriteEndElement();
427 }
428
429 return true;
430 }
431
441 public async Task<bool> RenderLatex(LatexRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
442 {
443 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
444 if (Info?.FileName is null)
445 return false;
446
447 if (Info.Dynamic)
448 Info.FileName = await Model.Multimedia.ImageContent.GetTemporaryFile(Info.DynamicContent, DefaultFileExtension.Substring(1));
449
450 StringBuilder Output = Renderer.Output;
451
452 Output.AppendLine("\\begin{figure}[h]");
453 Output.AppendLine("\\centering");
454
455 Output.Append("\\fbox{\\includegraphics{");
456 Output.Append(Info.FileName.Replace('\\', '/'));
457 Output.AppendLine("}}");
458
459 if (!string.IsNullOrEmpty(Info.Title))
460 {
461 Output.Append("\\caption{");
462 Output.Append(LatexRenderer.EscapeLaTeX(Info.Title));
463 Output.AppendLine("}");
464 }
465
466 Output.AppendLine("\\end{figure}");
467 Output.AppendLine();
468
469 return true;
470 }
471
479 public async Task<PixelInformation> GenerateImage(string[] Rows, string Language, MarkdownDocument Document)
480 {
481 GraphInfo Info = await GetFileName(Language, Rows, Document.Settings.Variables, DefaultFormat, DefaultQuality, DefaultFileExtension);
482 if (Info?.FileName is null)
483 return null;
484
485 byte[] Data = await Resources.ReadAllBytesAsync(Info.FileName);
486
487 using (SKBitmap Bitmap = SKBitmap.Decode(Data))
488 {
489 return new PixelInformationPng(Data, Bitmap.Width, Bitmap.Height);
490 }
491 }
492
498 public Grade Supports(XmlDocument Xml)
499 {
500 return Xml.DocumentElement.LocalName == Layout2DDocument.LocalName &&
501 Xml.DocumentElement.NamespaceURI == Layout2DDocument.Namespace ? Grade.Excellent : Grade.NotAtAll;
502 }
503
510 public async Task<object> TransformXml(XmlDocument Xml, Variables Session)
511 {
512 Layout2DDocument LayoutDoc = await Layout2DDocument.FromXml(Xml, Session);
513 RenderSettings Settings = await LayoutDoc.GetRenderSettings(Session);
514
515 KeyValuePair<SKImage, Map[]> P = await LayoutDoc.Render(Settings);
516 using (SKImage Img = P.Key) // TODO: Maps
517 {
518 return PixelInformation.FromImage(Img);
519 }
520 }
521
531 public async Task<bool> RenderContractXml(ContractsRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
532 {
533 string Xml = MarkdownDocument.AppendRows(Rows);
534
535 try
536 {
537 string Title;
538 int i = Language.IndexOf(':');
539
540 if (i > 0)
541 {
542 Title = Language.Substring(i + 1).Trim();
543 Language = Language.Substring(0, i).TrimEnd();
544 }
545 else
546 Title = string.Empty;
547
548 XmlDocument Doc = new XmlDocument();
549 Doc.LoadXml(Xml);
550
552 Layout2DDocument LayoutDoc = await Layout2DDocument.FromXml(Doc, Variables);
553 RenderSettings Settings = await LayoutDoc.GetRenderSettings(Variables);
554 XmlWriter Output = Renderer.XmlOutput;
555
556 KeyValuePair<SKImage, Map[]> P = await LayoutDoc.Render(Settings);
557
558 using (SKImage Img = P.Key)
559 {
560 Output.WriteStartElement("imageStandalone");
561
562 Output.WriteAttributeString("contentType", DefaultContentType);
563 Output.WriteAttributeString("width", Img.Width.ToString());
564 Output.WriteAttributeString("height", Img.Height.ToString());
565
566 using (SKData Data = Img.Encode(DefaultFormat, DefaultQuality))
567 {
568 byte[] Bin = Data.ToArray();
569
570 Output.WriteStartElement("binary");
571 Output.WriteValue(Convert.ToBase64String(Bin));
572 Output.WriteEndElement();
573 }
574
575 Output.WriteStartElement("caption");
576 if (string.IsNullOrEmpty(Title))
577 Output.WriteElementString("text", "Layout");
578 else
579 Output.WriteElementString("text", Title);
580
581 Output.WriteEndElement();
582 Output.WriteEndElement();
583 }
584
585 return true;
586 }
587 catch (XmlException ex)
588 {
589 ex = XML.AnnotateException(ex, Xml);
590 Log.Exception(ex);
591 return false;
592 }
593 catch (Exception ex)
594 {
595 Log.Exception(ex);
596 return false;
597 }
598 }
599
600 }
601}
Contains information about an emoji image.
Definition: ImageSource.cs:9
Image encoder/decoder.
Definition: ImageCodec.cs:14
const string FileExtensionPng
png
Definition: ImageCodec.cs:75
const string ContentTypePng
image/png
Definition: ImageCodec.cs:30
Renders Contracts XML from a Markdown document.
Renders LaTeX from a Markdown document.
static string EscapeLaTeX(string s)
Escapes text for output in a LaTeX document.
Class managing 2D XML Layout integration into Markdown documents.
Definition: XmlLayout.cs:41
Grade Supports(XmlDocument Xml)
Checks how well the handler supports multimedia content of a given type.
Definition: XmlLayout.cs:498
Grade Supports(string Language)
Checks how well the handler supports multimedia content of a given type.
Definition: XmlLayout.cs:146
async Task< object > TransformXml(XmlDocument Xml, Variables Session)
Transforms the XML document before visualizing it.
Definition: XmlLayout.cs:510
static void Init(string ContentRootFolder)
Initializes the Layout2D-Markdown integration.
Definition: XmlLayout.cs:64
async Task< bool > RenderHtml(HtmlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates HTML for the code content.
Definition: XmlLayout.cs:184
async Task< bool > RenderText(TextRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates plain text for the code content.
Definition: XmlLayout.cs:351
XmlLayout()
Class managing 2D XML Layout integration into Markdown documents.
Definition: XmlLayout.cs:55
async Task< bool > RenderMarkdown(MarkdownRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates Markdown for the code content.
Definition: XmlLayout.cs:327
static void DeleteOldFiles(TimeSpan MaxAge, bool Reschedule)
Deletes generated files older than MaxAge .
Definition: XmlLayout.cs:102
void Register(MarkdownDocument Document)
Is called on the object when an instance of the element has been created in a document.
Definition: XmlLayout.cs:170
async Task< bool > RenderContractXml(ContractsRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates smart contract XML for the code content.
Definition: XmlLayout.cs:531
async Task< bool > RenderLatex(LatexRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates LaTeX for the code content.
Definition: XmlLayout.cs:441
bool EvaluatesScript
If script is evaluated for this type of code block.
Definition: XmlLayout.cs:164
async Task< PixelInformation > GenerateImage(string[] Rows, string Language, MarkdownDocument Document)
Generates an image of the contents.
Definition: XmlLayout.cs:479
async Task< bool > RenderWpfXaml(WpfXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates WPF XAML for the code content.
Definition: XmlLayout.cs:368
async Task< bool > RenderXamarinFormsXaml(XamarinFormsXamlRenderer Renderer, string[] Rows, string Language, int Indent, MarkdownDocument Document)
Generates Xamarin.Forms XAML for the code content.
Definition: XmlLayout.cs:407
Contains a markdown document. This markdown document class supports original markdown,...
static string AppendRows(string[] Rows)
Appends a set of rows into a single string with newlines between rows.
MarkdownSettings Settings
Markdown settings.
Variables Variables
Collection of variables. Providing such a collection enables script execution inside markdown documen...
static async Task< bool > GenerateMarkdownFromFile(StringBuilder Output, string FileName, string Title)
Generates Markdown embedding an image available in a file.
Definition: ImageContent.cs:56
static string GenerateUrl(string Language, string[] Rows, out string ContentType, out string Title)
Generates a data URL of an encoded image.
static void GenerateMarkdown(StringBuilder Output, byte[] Bin, string ContentType, string Title)
Generates Markdown embedding an encoded image.
Definition: ImageContent.cs:82
Renders HTML from a Markdown document.
Definition: HtmlRenderer.cs:24
Renders portable Markdown from a Markdown document.
Abstract base class for Markdown renderers.
Definition: Renderer.cs:14
readonly StringBuilder Output
Renderer output.
Definition: Renderer.cs:18
Renders plain text from a Markdown document.
Definition: TextRenderer.cs:16
Renders XAML (WPF flavour) from a Markdown document.
Renders XAML (Xamarin.Forms flavour) from a Markdown document.
Static class managing loading of resources stored as embedded resources or in content files.
Definition: Resources.cs:15
static async Task< byte[]> ReadAllBytesAsync(string FileName)
Reads a binary file asynchronously.
Definition: Resources.cs:183
static Task WriteAllBytesAsync(string FileName, byte[] Data)
Creates a binary file asynchronously.
Definition: Resources.cs:216
Helps with common XML-related tasks.
Definition: XML.cs:19
static string HtmlValueEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe or the quote.
Definition: XML.cs:209
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
Definition: XML.cs:1588
static string HtmlAttributeEncode(string s)
Differs from Encode(String), in that it does not encode the aposotrophe.
Definition: XML.cs:119
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Definition: Log.cs:682
static void Informational(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an informational event.
Definition: Log.cs:334
Contains a 2D layout document.
bool Dynamic
If the layout is dynamic (i.e. contains script).
async Task< KeyValuePair< SKImage, Map[]> > Render(RenderSettings Settings)
Renders the layout to an image
static Task< Layout2DDocument > FromXml(string Xml, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
async Task< RenderSettings > GetRenderSettings(Variables Session)
Creates a render settings object.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:583
Class that can be used to schedule events in time. It uses a timer to execute tasks at the appointed ...
Definition: Scheduler.cs:26
void Dispose()
IDisposable.Dispose
Definition: Scheduler.cs:46
DateTime Add(DateTime When, ScheduledEventCallback Callback, object State)
Adds an event.
Definition: Scheduler.cs:66
Base class for graphs.
Definition: Graph.cs:79
const string GraphFgColorVariableName
Variable name for graph foreground color.
Definition: Graph.cs:98
const string GraphBgColorVariableName
Variable name for graph background color.
Definition: Graph.cs:93
static string ToRGBAStyle(SKColor Color)
Converts a color to an RGB(A) style string.
Definition: Graph.cs:903
Contains pixel information
static PixelInformation FromImage(SKImage Image)
Gets the pixel information from an SKImage.
Contains pixel information in PNG format
Contains information about a variable.
Definition: Variable.cs:10
Collection of variables.
Definition: Variables.cs:25
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Definition: Variables.cs:52
Contains methods for simple hash calculations.
Definition: Hashes.cs:59
static string ComputeSHA256HashString(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:328
Interface for all markdown handlers of code content that generates an image output.
Interface for all XML visalizers.
Interface for code content plain text renderers.
Interface for code content WPF XAML renderers.
Grade
Grade enumeration
Definition: Grade.cs:7