/* i-net software provides programming examples for illustration only, without warranty either expressed or implied, including, but not limited to, the implied warranties of merchantability and/or fitness for a particular purpose. This programming example assumes that you are familiar with the programming language being demonstrated and the tools used to create and debug procedures. i-net software support professionals can help explain the functionality of a particular procedure, but they will not modify these examples to provide added functionality or construct procedures to meet your specific needs. © i-net software 1998-2013 */ using System.Drawing.Drawing2D; using System.Drawing; using System; using Inet.Viewer.Resources; using System.Globalization; using System.IO; namespace Inet.Viewer.Data { /// /// Loader interface that is to fetch the data, to interpret the data and to give it back to the Viewer. /// This class is the link between the Viewer and IReportData . IReportData is the /// low level IO communication to the server. /// After the data are fetched they should be stored in and than invoked through /// /// The implemented Loader should override as this function is invoked for each token that is read. /// public class Loader { /// /// for the Property Data /// private byte[] data; /// /// /// protected int Offset { get; set; } /// /// /// protected int TokenSize { get; set; } /// /// /// protected int OldOffset { get; set; } /// /// Contains the byte data that will be processed /// public byte[] Data { get { return data; } set { data = value; } } /// /// Checks the byte data for the correct signature and checksum, and then goes through and /// interprets each individual token using load(token). /// internal void ReadTokens() { Offset = 0; if (data == null || data.Length < 4 || data[Offset++] != 26 || data[Offset++] != 88 || data[Offset++] != 77 || data[Offset++] != 76) { // wrong signature, empty or no data if (data == null) { data = new byte[0]; } string msg = GetHexDump(data, 0, System.Math.Min(data.Length, 200)); if (data.Length > 200) { msg += "\n................"; } else if (data.Length == 0) { msg = ""; } throw new ViewerException(0, strings.ErrorMessage_WrongDataReceived, null, null, null, null, 0, msg); } int length = Read4ByteInt(); if (length != data.Length) { throw new ViewerException(0, strings.ErrorMessage_DataLength, null, null, null, null, 0, "Header length:" + length + "\nPackage length:" + data.Length); } // check Checksum: at the end, Adler32 Checksumme for the whole byte arrays Offset = data.Length - 4; int checksum = Read4ByteInt(); Adler32 checker = new Adler32(); checker.Update(data, 0, data.Length - 4); int value = checker.Checksum; if (checksum != value) { throw new ViewerException(0, "There was an error in the data transmission: checksums differ.", null, null, null, null, 0, "expected: " + checksum + ", got: " + value); } // reset (after signature - 4 byte and lenght - 4 byte) Offset = 8; int size = data.Length - 4; // 4 Byte Checksumme while (Offset < size) { int token = ReadInt(); TokenSize = ReadInt(); OldOffset = Offset; if (!Load(token)) { return; // load(int) will be overriden by the implementation of this Loader } Offset = OldOffset + TokenSize; } } /// /// Reads a single token. Will be overridden by the implementations of this Loader. /// The lengt of a token ist handled one level up. /// ID of the token /// true: continue loading; false: stop loading protected internal virtual bool Load(int token) { switch (token) { case ViewerTokenConstants.TokenVersion: string engineVersion = ReadString(); string protocolVersion = ReadString(); try { double versionAsNumber = double.Parse(protocolVersion, CultureInfo.InvariantCulture); } catch (System.FormatException e) { ViewerUtils.PrintStackTrace(e); } break; case ViewerTokenConstants.TokenErrorMessage: ViewerException error = ReadError(); if (error != null) { throw error; } break; case ViewerTokenConstants.TokenPrompts: PromptData[] prompts = ReadPrompts(); if (prompts == null) { break; } throw new ViewerException(prompts); default: if (GetType() != typeof(Loader)) { ViewerUtils.Error("Loader.Load() Unknown token: " + token); } break; } return true; } /// /// Read the prompts /// /// an array or null protected internal PromptData[] ReadPrompts() { int numberOfPrompts = ReadInt(); if (numberOfPrompts == 0) { return null; } PromptData[] prompts = new PromptData[numberOfPrompts]; for (int i = 0; i < numberOfPrompts; i++) { bool needed = ReadInt() == 1; if (needed) { prompts[i] = new PromptData(ReadString(), ReadString(), ReadString(), ReadStringArray(), ReadStringArray(), ReadInt(), ReadInt() == 1, ReadInt() == 1, ReadInt() == 1, ReadInt() == 1, // prompt parameter name // prompt's sub report name // prompting parameter text // prompt parameter default value // default value descriptions // type (NO flags like range, etc.!) // discrete? // range? // multi? // default Values editable? // only show descriptions? // Informix SP? // edit mask // min length ReadInt() == 1, ReadInt() == 1, ReadString(), ReadString(), ReadString()); // max length prompts[i].CascadingParent = ReadString(); prompts[i].DisplayName = ReadString(); } else { prompts[i] = new PromptData(true); } } return prompts; } /// /// Reads the Error message from the byte Data and wraps it into a /// ViewerExeption class /// /// protected internal ViewerException ReadError() { int errorCode = Read4ByteInt(); string msg = ReadString(); string format = ReadString(); string srvVersion = ReadString(); string srvJVM = ReadString(); string srvOS = ReadString(); int srvCache = ReadInt(); string stacktrace = ReadString(); if (errorCode >= 0) { return new ViewerException(errorCode, msg, format, srvVersion, srvJVM, srvOS, srvCache, stacktrace); } return null; } /// /// String[]: [NUMBER_OF_STRINGS (int)] [STRING] [STRING] [STRING] ... /// String array encoded in the byte array protected internal string[] ReadStringArray() { string[] resultArray = new string[ReadInt()]; for (int i = 0; i < resultArray.Length; i++) { resultArray[i] = ReadString(); } return resultArray; } /// /// float[]: [NUMBER_OF_FLOATS (int)] [FLOAT] [FLOAT] [FLOAT] ... /// Float array encoded in the byte array. protected internal float[] ReadFloatArray() { int count = ReadInt(); if (count < 0) { return null; } float[] result = new float[count]; for (int i = 0; i < result.Length; i++) { result[i] = ReadFloat(); } return result; } /// /// Reads an encoded int /// Int encoded in the byte array protected internal int ReadInt() { byte b1 = data[Offset]; int result = data[Offset++] & 255; if (result == 0) { return -1; } int size; int bitmask = 128; // single bit on the left for (size = 1; size < 8; size++) { if ((result & bitmask) == bitmask) { break; } bitmask >>= 1; } if (size > 4) { // negative number size -= 4; result ^= bitmask; result -= bitmask; } else { result ^= bitmask; } for (int i = 1; i < size; i++) { result = (result << 8) | (data[Offset++] & 255); } return result; } /// /// Reads a boolean Flag /// protected internal bool ReadBoolean() { return ReadInt() != 0; } /// /// Reads an int encoded in classic 4-byte mode /// Int encoded as 4-byte int from the byte array protected internal int Read4ByteInt() { return (data[Offset++] << 24) + ((data[Offset++] & 255) << 16) + ((data[Offset++] & 255) << 8) + (data[Offset++] & 255); } /// /// Reads a float encoded as a classic 4-byte float /// Float encoded as 4-byte protected internal float ReadFloat() { // Read4ByteInt inversive byte part1 = data[Offset++]; byte part2 = data[Offset++]; byte part3 = data[Offset++]; byte part4 = data[Offset++]; byte[] intReverse = { part4, part3, part2, part1 }; float f = BitConverter.ToSingle(intReverse, 0); return f; } /// /// Reads a long encoded as a classic 8-byte long /// Long encoded as 8-byte protected internal long Read8ByteLong() { return ((long)Read4ByteInt()) << 32 | Read4ByteInt() & 0xFFFFFFFFL; } /// /// Reads a double encoded as a classic 8-byte double /// double encoded as 8-byte private double ReadDouble() { long l = Read8ByteLong(); double d = BitConverter.Int64BitsToDouble(l); return d; } /// /// String: [NUMBEROFCHARS] [CHAR (2-byte)] [CHAR (2-byte)] [CHAR (2-byte)] ... /// protected internal string ReadString() { int length = ReadInt(); if (length < 0) { return null; } char[] chars = new char[length]; for (int i = 0; i < length; i++) { chars[i] = (char)((data[Offset++] & 255) << 8 | (data[Offset++] & 255)); } return new string(chars); } /// /// Equivalent to readInt. /// Int protected internal int ReadTwip() { return ReadInt(); } /// /// Polyon: [COUNT_OF_POINTS] [X (int)] [Y (int)] [X (int)] [Y (int)] ... /// A Ploygon with a Point array protected internal Point[] ReadPolygon() { int count = ReadInt(); int[] xpoints = new int[count]; int[] ypoints = new int[count]; for (int i = 0; i < count; i++) { xpoints[i] = ReadTwip(); ypoints[i] = ReadTwip(); } return new Point[3]; } /// /// Shape: [NUMBER_OF_POINTS] [TYPE_OF_MOVEMENT] [PARAMS] [TYPE_OF_MOVEMENT] [PARAMS] ... /// Shape encoded in the byte array protected internal GraphicsPath ReadShape() { GraphicsPath path = new GraphicsPath(); path.FillMode = (FillMode)ReadInt(); float xPrev = 0; float yPrev = 0; while (true) { int type = ReadInt(); switch (type) { case -1: return path; case 0: // SEG_MOVETO xPrev = ReadFloat(); yPrev = ReadFloat(); path.StartFigure(); break; case 1: // SEG_LINETO float x = ReadFloat(); float y = ReadFloat(); path.AddLine(xPrev, yPrev, x, y); xPrev = x; yPrev = y; break; case 2: // SEG_QUADTO: float x1 = ReadFloat(); float y1 = ReadFloat(); float x2 = ReadFloat(); float y2 = ReadFloat(); path.AddBezier(xPrev, yPrev, (xPrev + x1 * 2f) / 3f, (yPrev + y1 * 2f) / 3f, (x2 + x1 * 2f) / 3f, (y2 + y1 * 2f) / 3f, x2, y2); xPrev = x2; yPrev = y2; break; case 3: // SEG_CUBICTO: x1 = ReadFloat(); y1 = ReadFloat(); x2 = ReadFloat(); y2 = ReadFloat(); float x3 = ReadFloat(); float y3 = ReadFloat(); path.AddBezier(xPrev, yPrev, x1, y1, x2, y2, x3, y3); xPrev = x3; yPrev = y3; break; case 4: // SEG_CLOSE: path.CloseFigure(); break; } } } /// /// Redas a Gradient Brush from the byte data /// protected internal LinearGradientBrush ReadGradientPaint() { PointF p1 = new PointF(ReadFloat(), ReadFloat()); Color c1 = Color.FromArgb(Read4ByteInt()); PointF p2 = new PointF(ReadFloat(), ReadFloat()); Color c2 = Color.FromArgb(Read4ByteInt()); // Cyclic is not used at the moment bool isCyclic = ReadBoolean(); LinearGradientBrush linear = new LinearGradientBrush(p1, p2, c1, c2); return linear; } /// /// Reads a TextureBrush from the byte data /// protected internal TextureBrush ReadTexturePaint(float alphaComposite) { RectangleF anchor = new Rectangle(); anchor.X = ReadFloat(); anchor.Y = ReadFloat(); anchor.Width = ReadFloat(); anchor.Height = ReadFloat(); byte[] imgBytes = ReadByteArray(); Image img = Image.FromStream(new MemoryStream(imgBytes)); System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes(); TextureBrush brush = new TextureBrush(img, new Rectangle(0, 0, img.Width, img.Height), attr); brush.TranslateTransform(anchor.X, anchor.Y); brush.ScaleTransform(anchor.Width / img.Width, anchor.Height / img.Height); brush.WrapMode = WrapMode.Tile; return brush; } /// /// Redas a Gradient Brush from the byte data /// And adds the alphaComposite as alpha value /// protected internal LinearGradientBrush ReadGradientPaint(float alphaComposite) { PointF p1 = new PointF(ReadFloat(), ReadFloat()); Color c1 = Color.FromArgb(Read4ByteInt()); PointF p2 = new PointF(ReadFloat(), ReadFloat()); Color c2 = Color.FromArgb(Read4ByteInt()); // if alpha is set to transparency if (alphaComposite < 1) { c1 = Color.FromArgb((int)(alphaComposite * 256), c1); c2 = Color.FromArgb((int)(alphaComposite * 256), c2); } bool isCyclic = ReadBoolean(); if (isCyclic) { LinearGradientBrush linear = new LinearGradientBrush(p1, p2, c1, c2); linear.WrapMode = WrapMode.TileFlipXY; return linear; } else { // code from IKVM graphics.setPaint() // a exact solution will calculate the size of the Graphics with the current transform float diffX = p2.X - p1.X; float diffY = p2.Y - p1.Y; const float Z = 60; // HACK zoom factor, with a larger factor .NET will make the gradient wider. LinearGradientBrush linear = new LinearGradientBrush( new PointF(p1.X - Z * diffX, p1.Y - Z * diffY), new PointF(p2.X + Z * diffX, p2.Y + Z * diffY), c1, c2); ColorBlend colorBlend = new ColorBlend(4); Color[] colors = colorBlend.Colors; colors[0] = colors[1] = c1; colors[2] = colors[3] = c2; float[] positions = colorBlend.Positions; positions[1] = Z / (2 * Z + 1); positions[2] = (Z + 1) / (2 * Z + 1); positions[3] = 1.0f; linear.InterpolationColors = colorBlend; return linear; } } /// /// Byte Array: [NUMBER OF BYTES] [BYTE] [BYTE] [BYTE] ... /// Byte array encoded in the data, or null if no byte array could be read protected internal byte[] ReadByteArray() { int count = ReadInt(); // data length if (count < 1 || count > data.Length - Offset) { ViewerUtils.PrintStackTrace(new System.Exception("Invalid byte array size: " + count + ". Amount of available bytes: " + (data.Length - Offset))); return null; } else { byte[] byteData = new byte[count]; // Image-Data System.Array.Copy(data, Offset, byteData, 0, count); int code = 0; // Image img = readImage(imgBytes); // -> readIndexImage oder readDirectImage for (int i = 0; i < count; i++) { code += byteData[i]; } return byteData; } } /// /// AffineTransform: m00, m10, m20, m01, m11, m21 /// AffineTransform encoded in the byte array protected internal Matrix ReadTransform() { return new Matrix((float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble(), (float)ReadDouble()); } /// /// Generates a hex dump as a string in error cases /// Buffer to dump /// Start offset to dump /// How many bytes to dump /// Hex dump as String protected static internal string GetHexDump(byte[] buf, int start, int count) { System.Text.StringBuilder sb = new System.Text.StringBuilder(count); byte[] buffer = (byte[])buf.Clone(); count += start; for (int i = start; i < count; i += 16) { sb.Append(Hex((byte)(i >> 8))).Append(Hex((byte)i)).Append(" "); FormatHexDump(buffer, i, count - i > 16 ? 16 : count - i, sb); } sb.Append("\r\n"); return sb.ToString(); } /// /// Format the byte buffer as hex dump. /// /// the byte buffer to dump /// the first index of the byte array /// the length /// the string builder to write to private static void FormatHexDump(byte[] buffer, int offset, int count, System.Text.StringBuilder sb) { int i; for (i = offset; i < offset + count; i++) { sb.Append(Hex(buffer[i])).Append(' '); if (buffer[i] < 32) { buffer[i] = 46; } } for (; i < offset + 16; i++) { sb.Append(" "); } sb.Append(' '); sb.Append(System.Text.Encoding.UTF8.GetString(buffer, offset, count)); sb.Append("\r\n"); } /// /// Converts a byte to a char. /// /// value to convert /// string representation of the byte private static string Hex(byte wert) { string a; int lo = (wert & 240) / 16; if (lo < 10) { a = lo.ToString(); } else { a = string.Empty + (char)(55 + lo); } lo = wert & 15; if (lo < 10) { a = a + lo.ToString(); } else { a = a + (char)(55 + lo); } return a; } } }