5using System.Reflection;
7using System.Threading.Tasks;
35 public const string Namespace =
"http://waher.se/Schema/Layout2D.xsd";
42 private static Dictionary<string, ILayoutElement> elementTypes =
new Dictionary<string, ILayoutElement>();
43 private static bool initialized =
false;
45 private readonly Dictionary<string, ILayoutElement> elementsById =
new Dictionary<string, ILayoutElement>();
46 private readonly Dictionary<string, object> attachments =
new Dictionary<string, object>(StringComparer.CurrentCultureIgnoreCase);
59 this.session = Session;
61 if (!(Attachments is
null))
63 foreach (KeyValuePair<string, object> P
in Attachments)
64 this.attachments[P.Key] = P.Value;
68 internal async Task<ILayoutElement> CreateElement(XmlElement Xml,
ILayoutElement Parent)
70 string Key = Xml.NamespaceURI +
"#" + Xml.LocalName;
79 if (Id.
Ok && !
string.IsNullOrEmpty(Id.
Result))
80 this.AddElementId(Id.
Result, Result);
90 public static Task<Layout2DDocument>
FromFile(
string FileName, params KeyValuePair<string, object>[] Attachments)
92 return FromFile(FileName,
true, Attachments);
101 public static Task<Layout2DDocument>
FromFile(
string FileName,
bool Preprocess, params KeyValuePair<string, object>[] Attachments)
113 public static async Task<Layout2DDocument>
FromFile(
string FileName,
bool Preprocess,
Variables Session, params KeyValuePair<string, object>[] Attachments)
116 return await
FromXml(Xml, Preprocess, Session, Attachments);
125 public static Task<Layout2DDocument>
FromStream(Stream Input, Encoding DefaultEncoding, params KeyValuePair<string, object>[] Attachments)
127 return FromStream(Input, DefaultEncoding,
true, Attachments);
137 public static Task<Layout2DDocument>
FromStream(Stream Input, Encoding DefaultEncoding,
bool Preprocess, params KeyValuePair<string, object>[] Attachments)
150 public static Task<Layout2DDocument>
FromStream(Stream Input, Encoding DefaultEncoding,
bool Preprocess,
Variables Session, params KeyValuePair<string, object>[] Attachments)
152 long c = Input.Length - Input.Position;
153 if (c >
int.MaxValue)
154 throw new OutOfMemoryException(
"Input too large");
157 byte[] Bin =
new byte[c2];
158 Input.ReadAll(Bin, 0, c2);
162 return FromXml(Xml, Preprocess, Session, Attachments);
170 public static Task<Layout2DDocument>
FromXml(
string Xml, params KeyValuePair<string, object>[] Attachments)
172 return FromXml(Xml,
true, Attachments);
181 public static Task<Layout2DDocument>
FromXml(
string Xml,
bool Preprocess, params KeyValuePair<string, object>[] Attachments)
193 public static async Task<Layout2DDocument>
FromXml(
string Xml,
bool Preprocess,
Variables Session, params KeyValuePair<string, object>[] Attachments)
204 catch (XmlException ex)
209 return await
FromXml(Doc, Session, Attachments);
217 public static Task<Layout2DDocument>
FromXml(XmlDocument Xml, params KeyValuePair<string, object>[] Attachments)
228 public static Task<Layout2DDocument>
FromXml(XmlDocument Xml,
Variables Session, params KeyValuePair<string, object>[] Attachments)
230 return FromXml(Xml.DocumentElement, Session, Attachments);
238 public static Task<Layout2DDocument>
FromXml(XmlElement Xml, params KeyValuePair<string, object>[] Attachments)
249 public static async Task<Layout2DDocument>
FromXml(XmlElement Xml,
Variables Session, params KeyValuePair<string, object>[] Attachments)
252 throw new ArgumentException(
"XML does not represent a 2D layout document.", nameof(Xml));
259 Dictionary<string, ILayoutElement> TypesPerKey =
new Dictionary<string, ILayoutElement>();
262 foreach (Type T
in LayoutElementTypes)
264 TypeInfo TI = T.GetTypeInfo();
265 if (TI.IsAbstract || TI.IsInterface || TI.IsGenericTypeDefinition)
271 string Key = E.Namespace +
"#" + E.
LocalName;
273 if (TypesPerKey.ContainsKey(Key))
274 Log.
Error(
"Layout element type already defined: " + Key);
276 TypesPerKey[Key] = E;
284 elementTypes = TypesPerKey;
287 Types.OnInvalidated += (Sender, e) => initialized =
false;
292 Result.root = await Result.CreateElement(Xml,
null);
312 return !(Xml is
null) &&
313 Xml.LocalName == LocalName &&
314 Xml.NamespaceURI == Namespace;
320 [Obsolete(
"Use DisposeAsync() instead.")]
331 this.root?.Dispose();
333 foreach (
object Attachment
in this.attachments.Values)
336 await DisposableAsync.DisposeAsync();
337 else if (Attachment is IDisposable Disposable)
338 Disposable.Dispose();
366 Width = Settings.
Width;
371 SKSurface Surface = SKSurface.Create(
new SKImageInfo(Width, Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul));
375 SKCanvas Canvas = Surface.
Canvas;
376 State =
new DrawingState(Canvas, Settings, this.session);
383 while (!(this.root is
null))
396 throw new InvalidOperationException(
"Layout positions not well defined. Dimensions diverge:\r\n\r\n" + ShortestBranch);
409 if (!(this.root is
null))
411 Width = (int)this.root.
Right - (
int)this.root.Left + 1;
412 Height = (int)this.root.
Bottom - (
int)this.root.Top - 1;
415 Surface = SKSurface.Create(
new SKImageInfo(Width, Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul));
417 throw new InvalidOperationException(
"Unable to render layout.");
419 Canvas = Surface.Canvas;
420 State.Canvas = Canvas;
425 if (!(this.root is
null) && this.root.
Left.HasValue &&
this.root.Top.HasValue)
426 State.
Canvas.Translate(-this.root.
Left.Value, -
this.root.Top.Value);
430 if (!(this.root is
null))
432 if (this.root.
Width.HasValue &&
this.root.Height.HasValue)
434 float Width2 = this.root.Width.Value + 1;
435 float Height2 = this.root.Height.Value + 1;
436 float ScaleX = Width / Width2;
437 float ScaleY = Height / Height2;
441 State.
Canvas.Translate(0, (Height - Height2 * ScaleX) / 2);
442 State.
Canvas.Scale(ScaleX);
444 else if (ScaleY < ScaleX)
446 State.
Canvas.Translate((Width - Width2 * ScaleY) / 2, 0);
447 State.
Canvas.Scale(ScaleY);
451 if (this.root.
Left.HasValue &&
this.root.Top.HasValue)
452 State.
Canvas.Translate(-this.root.
Left.Value, -
this.root.Top.Value);
457 if (!(this.root is
null))
458 await this.root.
Draw(State);
460 return new KeyValuePair<SKImage, Map[]>(Surface.Snapshot(), Maps);
503 public event EventHandler<UpdatedEventArgs>
OnUpdated =
null;
527 return this.attachments.TryGetValue(ContentId, out Content);
537 if (this.attachments.TryGetValue(ContentId, out
object Obj))
539 this.attachments.Remove(ContentId);
542 await DisposableAsync.DisposeAsync();
543 else if (Obj is IDisposable Disposable)
544 Disposable.Dispose();
563 ContentId = Guid.NewGuid().ToString();
565 while (this.attachments.ContainsKey(ContentId));
567 this.attachments[ContentId] = Content;
577 public async Task
AddContent(
string ContentId,
object Content)
580 this.attachments[ContentId] = Content;
590 this.elementsById[Id] = Element;
601 return this.elementsById.TryGetValue(Id, out Element);
609 this.elementsById.Clear();
621 ImageSize = RenderedImageSize.ResizeImage
624 if (this.root is Model.Layout2D
Layout2D)
633 float w = Result.
Width;
635 Result.Width = (int)(w + 0.5f);
643 Result.Height = (int)(h + 0.5f);
648 if (BackgroundId.
Ok &&
654 Result.BackgroundColor = Color.
Result;
668 KeyValuePair<string, object>[] Attachments =
new KeyValuePair<string, object>[this.attachments.Count];
671 foreach (KeyValuePair<string, object> P
in this.attachments)
672 Attachments[i++] = P;
676 root = this.root.
Copy(
null),
710 StringBuilder Output =
new StringBuilder();
712 return Output.ToString();
720 public void ExportState(StringBuilder Output, XmlWriterSettings Settings)
722 using (XmlWriter w = XmlWriter.Create(Output, Settings))
735 Output.WriteStartElement(
"Layout2DState",
"http://waher.se/Schema/Layout2DState.xsd");
737 Output.WriteEndElement();
745 internal async Task<SKImage> RaiseGetInternalImage(
string ContentId)
748 await this.OnGetInternalImage.Raise(
this, e);
Helps with common XML-related tasks.
static XmlException AnnotateException(XmlException ex)
Creates a new XML Exception object, with reference to the source XML file, for information.
static XmlWriterSettings WriterSettings(bool Indent, bool OmitXmlDeclaration)
Gets an XML writer settings object.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Static class managing the application event log. Applications and services log events on this static ...
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.
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.
Event raised when the layout model has been Drawing internally.
Event raised when the layout model has been updated internally.
SKImage Image
Image to return.
Event raised when the layout model has been updated internally.
Syntax-related layout exception
Contains a 2D layout document.
EventHandlerAsync< InteralImageEventArgs > OnGetInternalImage
Event raised when an internal image is requested, and one is not found in the document or variables.
async Task DisposeAsync()
IDisposableAsync.DisposeAsync
const string LocalName
Layout2D
Layout2DDocument Copy(Variables Session)
Makes a copy of the layout document.
static Task< Layout2DDocument > FromStream(Stream Input, Encoding DefaultEncoding, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a stream.
void ClearElementIds()
Clears registered elements with IDs.
bool Dynamic
If the layout is dynamic (i.e. contains script).
void RaiseUpdated(ILayoutElement Element)
Raises the OnUpdated event.
static Task< Layout2DDocument > FromStream(Stream Input, Encoding DefaultEncoding, bool Preprocess, Variables Session, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a stream.
static Task< Layout2DDocument > FromFile(string FileName, bool Preprocess, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a file.
void RaiseMeasuringDimensions(DrawingState State)
Raises the OnMeasuringDimensions event.
bool SupportsAsynchronnousUpdates
If asynchronous updates are supported.
void ExportState(StringBuilder Output, XmlWriterSettings Settings)
Exports the internal state of the layout.
void AddElementId(string Id, ILayoutElement Element)
Adds an element with an ID
string ExportState()
Exports the internal state of the layout.
void ExportState(XmlWriter Output)
Exports the internal state of the layout.
EventHandler< DrawingEventArgs > OnMeasuringDimensions
Event raised when the layout dimensions are being measured. Event can be raised multiple times during...
static Task< Layout2DDocument > FromXml(string Xml, bool Preprocess, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
static Task< Layout2DDocument > FromXml(XmlDocument Xml, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
static async Task< Layout2DDocument > FromXml(XmlElement Xml, Variables Session, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
bool TryGetElement(string Id, out ILayoutElement Element)
Tries to get a layout element, given an ID reference
string AddContent(object Content)
Adds content to the layout.
EventHandler< UpdatedEventArgs > OnUpdated
Event raised when the internal state of the layout has been updated.
async Task< KeyValuePair< SKImage, Map[]> > Render(RenderSettings Settings)
Renders the layout to an image
static readonly string SchemaResourceName
Schema resource name.
static Task< Layout2DDocument > FromXml(XmlDocument Xml, Variables Session, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
static bool IsLayoutXml(XmlDocument Xml)
Checks if an XML document contains a layout document.
static bool IsLayoutXml(XmlElement Xml)
Checks if an XML element contains a layout document.
async Task< bool > DisposeContent(string ContentId)
Disposes of attached content, given its ID.
EventHandler< DrawingEventArgs > OnMeasuringPositions
Event raised when the layout positions are being measured. Event is eaised once after dimensions have...
async Task AddContent(string ContentId, object Content)
Adds content to the layout.
const string Namespace
http://waher.se/Schema/Layout2D.xsd
static Task< Layout2DDocument > FromFile(string FileName, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a file.
static Task< Layout2DDocument > FromXml(string Xml, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
static async Task< Layout2DDocument > FromXml(string Xml, bool Preprocess, Variables Session, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
bool TryGetContent(string ContentId, out object Content)
Tries to get content from attached content.
static Task< Layout2DDocument > FromStream(Stream Input, Encoding DefaultEncoding, bool Preprocess, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a stream.
void Dispose()
IDisposable.Dispose
void RaiseMeasuringPositions(DrawingState State)
Raises the OnMeasuringPositions event.
string ExportState(XmlWriterSettings Settings)
Exports the internal state of the layout.
async Task< RenderSettings > GetRenderSettings(Variables Session)
Creates a render settings object.
static Task< Layout2DDocument > FromXml(XmlElement Xml, params KeyValuePair< string, object >[] Attachments)
Parses a 2D layout document from its XML definition.
static async Task< Layout2DDocument > FromFile(string FileName, bool Preprocess, Variables Session, params KeyValuePair< string, object >[] Attachments)
Loads a 2D layout document from a file.
Contains information about an actionable area in a generated image.
Manages an attribute value or expression.
bool Defined
If the attribute is defined.
static async Task< EvaluationResult< T > > TryEvaluate(Attribute< T > Attribute, Variables Session)
Tries to evaluate the attribute value.
Result of asynchronous evaluation.
T Result
Evaluated result, if successful.
bool Ok
If evaluation was successful.
ColorAttribute ColorAttribute
Color
SKCanvas Canvas
Current drawing canvas.
void Dispose()
IDisposable.Dispose
void CalcDrawingSize(Length L, ref float Size, bool Horizontal, ILayoutElement Element)
Converts a defined length to drawing size.
string GetShortestRelativeMeasurementStateXml()
Gets the shortest subtree State XML of an element with relative measurements.
void ClearRelativeMeasurement(bool LogRelativeElements)
Clears information about first relative measurement.
bool MeasureRelative
If layout contains relative sizes and dimensions should be recalculated.
Root node for two-dimensional layouts
StringAttribute BackgroundColorAttribute
Background Color
LengthAttribute WidthAttribute
Width
LengthAttribute HeightAttribute
Height
SKColor BackgroundColor
Background color
int Height
Height of image
RenderedImageSize ImageSize
Offset along X-axis.
static async Task< string > ReadAllTextAsync(string FileName)
Reads a text file asynchronously.
Static class managing binary representations of strings.
static string GetString(byte[] Data, int Offset, int Count, Encoding DefaultEncoding)
Gets a string from its binary representation, taking any Byte Order Mark (BOM) into account.
Static class that dynamically manages types and interfaces available in the runtime environment.
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Class managing a script expression.
static Task< string > TransformAsync(string s, string StartDelimiter, string StopDelimiter, Variables Variables)
Transforms a string by executing embedded script.
Interface for asynchronously disposable objects.
Base interface for all layout elements.
float? Bottom
Bottom coordinate of bounding box, after measurement.
float? Width
Width of element
Task RegisterIDs(Variables Session)
Registers any IDs defined with the encapsulating document.
ILayoutElement Copy(ILayoutElement Parent)
Creates a copy of the layout element.
float? Right
Right coordinate of bounding box, after measurement.
Task Draw(DrawingState State)
Draws layout entities.
Task MeasureDimensions(DrawingState State)
Measures layout entities and defines unassigned properties, related to dimensions....
string LocalName
Local name of type of element.
Task FromXml(XmlElement Input)
Populates the element (including children) with information from its XML definition.
float? Left
Left coordinate of bounding box, after measurement.
ILayoutElement Create(Layout2DDocument Document, ILayoutElement Parent)
Creates a new instance of the layout element.
string ExportState()
Exports the internal state of the layout.
void MeasurePositions(DrawingState State)
Measures layout entities and defines unassigned properties, related to positions.
StringAttribute IdAttribute
ID Attribute
RenderedImageSize
Affects the size of the rendered image.