diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseAssemblyInitializer.cs b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseAssemblyInitializer.cs new file mode 100644 index 00000000..3b8ef575 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseAssemblyInitializer.cs @@ -0,0 +1,17 @@ +using FastReport.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FastReport.ClickHouse +{ + public class ClickHouseAssemblyInitializer : AssemblyInitializerBase + { + public ClickHouseAssemblyInitializer() + { + RegisteredObjects.AddConnection(typeof(ClickHouseDataConnection),"ClickHouse"); + } + } +} diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnection.DesignExt.cs b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnection.DesignExt.cs new file mode 100644 index 00000000..46e0472d --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnection.DesignExt.cs @@ -0,0 +1,37 @@ +using ClickHouse.Client.ADO; +using ClickHouse.Client.Types; +using FastReport.Data; +using FastReport.Data.ConnectionEditors; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FastReport.ClickHouse +{ + public partial class ClickHouseDataConnection + { + public override Type GetParameterType() + { + return typeof(ClickHouseTypeCode); + } + public override ConnectionEditorBase GetEditor() + { + return new ClickHouseConnectionEditor(); + } + public override string GetConnectionId() + { + ClickHouseConnectionStringBuilder builder = new ClickHouseConnectionStringBuilder(ConnectionString); + string info = ""; + try + { + info = builder.Database; + } + catch + { + } + return "ClickHouse: " + info; + } + } +} diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.Designer.cs new file mode 100644 index 00000000..c1c49977 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.Designer.cs @@ -0,0 +1,196 @@ +namespace FastReport.Data +{ + partial class ClickHouseConnectionEditor + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new FastReport.Controls.LabelLine(); + this.gbServer = new System.Windows.Forms.GroupBox(); + this.tbPort = new System.Windows.Forms.TextBox(); + this.port = new System.Windows.Forms.Label(); + this.lblServer = new System.Windows.Forms.Label(); + this.tbServer = new System.Windows.Forms.TextBox(); + this.tbUserName = new System.Windows.Forms.TextBox(); + this.tbPassword = new System.Windows.Forms.TextBox(); + this.lblUserName = new System.Windows.Forms.Label(); + this.lblPassword = new System.Windows.Forms.Label(); + this.gbDatabase = new System.Windows.Forms.GroupBox(); + this.lblDatabase = new System.Windows.Forms.Label(); + this.tbDatabase = new System.Windows.Forms.TextBox(); + this.gbServer.SuspendLayout(); + this.gbDatabase.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(8, 278); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(320, 17); + this.label1.TabIndex = 2; + // + // gbServer + // + this.gbServer.Controls.Add(this.tbPort); + this.gbServer.Controls.Add(this.port); + this.gbServer.Controls.Add(this.lblServer); + this.gbServer.Controls.Add(this.tbServer); + this.gbServer.Controls.Add(this.tbUserName); + this.gbServer.Controls.Add(this.tbPassword); + this.gbServer.Controls.Add(this.lblUserName); + this.gbServer.Controls.Add(this.lblPassword); + this.gbServer.Location = new System.Drawing.Point(8, 16); + this.gbServer.Name = "gbServer"; + this.gbServer.Size = new System.Drawing.Size(320, 156); + this.gbServer.TabIndex = 4; + this.gbServer.TabStop = false; + this.gbServer.Text = "Server"; + // + // tbPort + // + this.tbPort.Location = new System.Drawing.Point(120, 74); + this.tbPort.Name = "tbPort"; + this.tbPort.Size = new System.Drawing.Size(188, 20); + this.tbPort.TabIndex = 6; + // + // port + // + this.port.AutoSize = true; + this.port.Location = new System.Drawing.Point(12, 78); + this.port.Name = "port"; + this.port.Size = new System.Drawing.Size(66, 13); + this.port.TabIndex = 5; + this.port.Text = "Server port:"; + // + // lblServer + // + this.lblServer.AutoSize = true; + this.lblServer.Location = new System.Drawing.Point(12, 20); + this.lblServer.Name = "lblServer"; + this.lblServer.Size = new System.Drawing.Size(72, 13); + this.lblServer.TabIndex = 4; + this.lblServer.Text = "Server name:"; + // + // tbServer + // + this.tbServer.Location = new System.Drawing.Point(12, 40); + this.tbServer.Name = "tbServer"; + this.tbServer.Size = new System.Drawing.Size(296, 20); + this.tbServer.TabIndex = 0; + // + // tbUserName + // + this.tbUserName.Location = new System.Drawing.Point(120, 101); + this.tbUserName.Name = "tbUserName"; + this.tbUserName.Size = new System.Drawing.Size(188, 20); + this.tbUserName.TabIndex = 1; + // + // tbPassword + // + this.tbPassword.Location = new System.Drawing.Point(120, 125); + this.tbPassword.Name = "tbPassword"; + this.tbPassword.Size = new System.Drawing.Size(188, 20); + this.tbPassword.TabIndex = 2; + this.tbPassword.UseSystemPasswordChar = true; + // + // lblUserName + // + this.lblUserName.AutoSize = true; + this.lblUserName.Location = new System.Drawing.Point(12, 105); + this.lblUserName.Name = "lblUserName"; + this.lblUserName.Size = new System.Drawing.Size(62, 13); + this.lblUserName.TabIndex = 0; + this.lblUserName.Text = "User name:"; + // + // lblPassword + // + this.lblPassword.AutoSize = true; + this.lblPassword.Location = new System.Drawing.Point(12, 129); + this.lblPassword.Name = "lblPassword"; + this.lblPassword.Size = new System.Drawing.Size(57, 13); + this.lblPassword.TabIndex = 1; + this.lblPassword.Text = "Password:"; + // + // gbDatabase + // + this.gbDatabase.Controls.Add(this.lblDatabase); + this.gbDatabase.Controls.Add(this.tbDatabase); + this.gbDatabase.Location = new System.Drawing.Point(8, 187); + this.gbDatabase.Name = "gbDatabase"; + this.gbDatabase.Size = new System.Drawing.Size(320, 76); + this.gbDatabase.TabIndex = 5; + this.gbDatabase.TabStop = false; + this.gbDatabase.Text = "Database"; + // + // lblDatabase + // + this.lblDatabase.AutoSize = true; + this.lblDatabase.Location = new System.Drawing.Point(12, 20); + this.lblDatabase.Name = "lblDatabase"; + this.lblDatabase.Size = new System.Drawing.Size(57, 13); + this.lblDatabase.TabIndex = 3; + this.lblDatabase.Text = "Database:"; + // + // tbDatabase + // + this.tbDatabase.Location = new System.Drawing.Point(12, 40); + this.tbDatabase.Name = "tbDatabase"; + this.tbDatabase.Size = new System.Drawing.Size(296, 20); + this.tbDatabase.TabIndex = 0; + // + // ClickHouseConnectionEditor + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.gbDatabase); + this.Controls.Add(this.gbServer); + this.Controls.Add(this.label1); + this.Name = "ClickHouseConnectionEditor"; + this.Size = new System.Drawing.Size(336, 358); + this.gbServer.ResumeLayout(false); + this.gbServer.PerformLayout(); + this.gbDatabase.ResumeLayout(false); + this.gbDatabase.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + private FastReport.Controls.LabelLine label1; + private System.Windows.Forms.GroupBox gbServer; + private System.Windows.Forms.Label lblServer; + private System.Windows.Forms.TextBox tbServer; + private System.Windows.Forms.TextBox tbUserName; + private System.Windows.Forms.TextBox tbPassword; + private System.Windows.Forms.Label lblUserName; + private System.Windows.Forms.Label lblPassword; + private System.Windows.Forms.GroupBox gbDatabase; + private System.Windows.Forms.Label lblDatabase; + private System.Windows.Forms.TextBox tbDatabase; + private System.Windows.Forms.TextBox tbPort; + private System.Windows.Forms.Label port; + } +} diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.cs b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.cs new file mode 100644 index 00000000..db934f9d --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Text; +using System.Windows.Forms; +using FastReport.Data.ConnectionEditors; +using FastReport.Forms; +using FastReport.Utils; +using ClickHouse.Client.ADO; + +namespace FastReport.Data +{ + public partial class ClickHouseConnectionEditor : ConnectionEditorBase + { + private string FConnectionString; + private void Localize() + { + //for now is empty. + } + + protected override string GetConnectionString() + { + ClickHouseConnectionStringBuilder builder = new ClickHouseConnectionStringBuilder(FConnectionString); + + builder.Username = tbUserName.Text; + builder.Host = tbServer.Text; + builder.Port = ushort.Parse(tbPort.Text); + builder.Password = tbPassword.Text; + builder.Database = tbDatabase.Text; + + return builder.ToString(); + } + + protected override void SetConnectionString(string value) + { + FConnectionString = value; + ClickHouseConnectionStringBuilder builder = new ClickHouseConnectionStringBuilder(FConnectionString); + tbServer.Text = builder.Host; + tbUserName.Text = builder.Username; + tbPassword.Text = builder.Password; + tbDatabase.Text = builder.Database; + tbPort.Text = builder.Port.ToString(); + } + + public ClickHouseConnectionEditor() + { + InitializeComponent(); + Localize(); + } + + + } +} diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.resx b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.resx new file mode 100644 index 00000000..d58980a3 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseConnectionEditor.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseDataConnection.cs new file mode 100644 index 00000000..14c7cee5 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/ClickHouseDataConnection.cs @@ -0,0 +1,120 @@ +using ClickHouse.Client.ADO; +using ClickHouse.Client.ADO.Adapters; +using ClickHouse.Client.ADO.Parameters; +using ClickHouse.Client.ADO.Readers; +using ClickHouse.Client.Types; +using ClickHouse.Client.Utility; +using FastReport.Data; +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.Common; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FastReport.ClickHouse +{ + public partial class ClickHouseDataConnection : DataConnectionBase + { + private void GetDBObjectNames(string name, List list) + { + DataTable schema = null; + DbConnection connection = GetConnection(); + try + { + OpenConnection(connection); + schema = connection.GetSchema(name, new string[] { connection.Database }); + } + finally + { + DisposeConnection(connection); + } + foreach (DataRow row in schema.Rows) + { + list.Add(row["name"].ToString()); + } + } + public override string QuoteIdentifier(string value, DbConnection connection) + { + return "\"" + value + "\""; + } + public override Type GetConnectionType() + { + return typeof(ClickHouseConnection); + } + public override string[] GetTableNames() + { + List list = new List(); + GetDBObjectNames("Tables", list); + return list.ToArray(); + } + + public override DbDataAdapter GetAdapter(string selectCommand, DbConnection connection, CommandParameterCollection parameters) + { + ClickHouseDataAdapter clickHouseDataAdapter = new ClickHouseDataAdapter(); + var command = connection.CreateCommand() as ClickHouseCommand; + + foreach (CommandParameter p in parameters) + { + selectCommand = selectCommand.Replace($"@{p.Name}", $"{{{p.Name}:{(ClickHouseTypeCode)p.DataType}}}"); + command.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), p.Value); + } + command.CommandText = selectCommand; + clickHouseDataAdapter.SelectCommand = command; + return clickHouseDataAdapter; + } + private string PrepareSelectCommand(string selectCommand, string tableName, DbConnection connection) + { + if (String.IsNullOrEmpty(selectCommand)) + { + selectCommand = "select * from " + QuoteIdentifier(tableName, connection); + } + return selectCommand; + } + private IEnumerable GetColumns(ClickHouseDataReader reader) + { + for (int i = 0; i < reader.FieldCount; i++) + { + var columnType = reader.GetFieldType(i); + + yield return new DataColumn(reader.GetName(i), columnType); + } + } + + public override void FillTableSchema(DataTable table, string selectCommand, CommandParameterCollection parameters) + { + ClickHouseConnection clickHouseConnection = GetConnection() as ClickHouseConnection; + + try + { + OpenConnection(clickHouseConnection); + + selectCommand = PrepareSelectCommand(selectCommand, table.TableName, clickHouseConnection); + /*To reduce size of traffic and size of answer from ClickHouse server. + Because FillSchema doesn't work in this ADO.NET library. + LIMIT 0 gets an empy set, but we still have list of desired columns + Prorably can be a better way. + */ + selectCommand += " LIMIT 0"; + ClickHouseCommand clickHouseCommand = clickHouseConnection.CreateCommand(); + + foreach (CommandParameter p in parameters) + { + selectCommand = selectCommand.Replace($"@{p.Name}", $"{{{p.Name}:{(ClickHouseTypeCode)p.DataType}}}"); + clickHouseCommand.AddParameter(p.Name, ((ClickHouseTypeCode)p.DataType).ToString(), p.Value); + } + clickHouseCommand.CommandText = selectCommand; + using (ClickHouseDataReader reader = clickHouseCommand.ExecuteReader() as ClickHouseDataReader) + { + var clms = GetColumns(reader); + table.Columns.AddRange(clms.ToArray()); + } + } + finally + { + DisposeConnection(clickHouseConnection); + } + } + } +} diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/Directory.Build.targets b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/Directory.Build.targets new file mode 100644 index 00000000..18cedec9 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/Directory.Build.targets @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.ClickHouse.csproj b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.ClickHouse.csproj new file mode 100644 index 00000000..e46160af --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.ClickHouse.csproj @@ -0,0 +1,51 @@ + + + + net472 + true + Fast Reports Inc. + Fast Reports Inc. + https://www.fast-report.com/en/product/fast-report-net/license + https://www.fast-report.com/en/product/fast-report-net + Fast Reports Inc. + FastReport.Data.ClickHouse + Represents a connection to ClickHouse database for FastReport.Net. + FastReport.Data.ClickHouse + https://www.fast-report.com/download/images/frlogo-big.png + reporting, ClickHouse, connection, reports + 1.0.0 + Debug;Release; + FastReport.Data.ClickHouse + FastReport.Data + + + + + + + + + + all + + + + + + + + + + UserControl + + + ClickHouseConnectionEditor.cs + + + ClickHouseConnectionEditor.cs + Designer + + + diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.Core.Data.ClickHouse.csproj b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.Core.Data.ClickHouse.csproj new file mode 100644 index 00000000..5a8b3a3a --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.Core.Data.ClickHouse.csproj @@ -0,0 +1,43 @@ + + + + net472;netstandard2.1;netcoreapp2.2 + true + Fast Reports Inc. + Fast Reports Inc. + https://www.fast-report.com/en/product/fast-report-net/license + https://www.fast-report.com/en/product/fast-report-net + Fast Reports Inc. + FastReport.Data.ClickHouse + Represents a connection to ClickHouse database for FastReport .NET + FastReport.Core.Data.ClickHouse + https://www.fast-report.com/download/images/frlogo-big.png + reporting, ClickHouse, connection, reports + 1.0.0 + Debug;Release; + FastReport.Data.ClickHouse + FastReport.Data + + + + + + + + + + + + + + + + + + + + + + diff --git a/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.OpenSource.Data.ClickHouse.csproj b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.OpenSource.Data.ClickHouse.csproj new file mode 100644 index 00000000..4bbf40b1 --- /dev/null +++ b/Extras/Core/FastReport.Data/FastReport.ClickHouse/FastReport.ClickHouse/FastReport.OpenSource.Data.ClickHouse.csproj @@ -0,0 +1,43 @@ + + + + net472;netstandard2.1;netcoreapp2.2 + true + Fast Reports Inc. + Fast Reports Inc. + https://www.fast-report.com/en/product/fast-report-net/license + https://www.fast-report.com/en/product/fast-report-net + Fast Reports Inc. + FastReport.Data.ClickHouse + Represents a connection to ClickHouse database for FastReport .NET + FastReport.Core.Data.ClickHouse + https://www.fast-report.com/download/images/frlogo-big.png + reporting, ClickHouse, connection, reports + 1.0.0 + Debug;Release; + FastReport.Data.ClickHouse + FastReport.Data + + + + + + + + + + + + + + + + + + + + + + diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Couchbase/CouchbaseConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.Couchbase/CouchbaseConnectionEditor.Designer.cs index dd5578bf..4caca978 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.Couchbase/CouchbaseConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.Couchbase/CouchbaseConnectionEditor.Designer.cs @@ -144,7 +144,7 @@ private void InitializeComponent() // CouchbaseConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.gbDatabase); this.Controls.Add(this.gbServer); this.Controls.Add(this.labelLine1); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonConnectionEditor.cs b/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonConnectionEditor.cs index 8eeaea0d..066247eb 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonConnectionEditor.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonConnectionEditor.cs @@ -15,7 +15,7 @@ private void Localize() { gbSelect.Text = Res.Get("ConnectionEditors,Common,Database"); lblJsonPath.Text = Res.Get("ConnectionEditors,Json,Path"); - tbJsonPath.Image = Res.GetImage(1); + tbJsonPath.ImageIndex = 1; } private void tbFile_ButtonClick(object sender, EventArgs e) diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonDataConnection.cs index 426a05fd..cf8dd0c1 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonDataConnection.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.Json/JsonDataConnection.cs @@ -33,6 +33,7 @@ public string JsonData { get { + System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); if (string.IsNullOrEmpty(jsonData)) using (WebClient webClient = new WebClient()) { diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/FastReport.Data.MongoDB.csproj b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/FastReport.Data.MongoDB.csproj index 8e1e8597..cabe1e48 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/FastReport.Data.MongoDB.csproj +++ b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/FastReport.Data.MongoDB.csproj @@ -34,9 +34,7 @@ - - UserControl - + MongoDBConnectionEditor.cs diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBConnectionEditor.Designer.cs index bfe4bd79..db1108bf 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBConnectionEditor.Designer.cs @@ -214,7 +214,7 @@ private void InitializeComponent() // MongoDBConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.gbDatabase); this.Controls.Add(this.btnAdvanced); this.Controls.Add(this.gbServer); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBDataConnection.cs b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBDataConnection.cs index 069d7098..0c648c17 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBDataConnection.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.MongoDB/MongoDBDataConnection.cs @@ -64,9 +64,12 @@ protected DataTable CreateDataTable(DataTable table, bool allRows) var collection = db.GetCollection(table.TableName); if (!allRows) { - var documents = collection.Find(new BsonDocument()).First(); - DataRow dr = table.NewRow(); - ExecuteFillDataTable(documents, table, dr, string.Empty); + var documents = collection.Find(new BsonDocument()).FirstOrDefault(); + if (documents != null) + { + DataRow dr = table.NewRow(); + ExecuteFillDataTable(documents, table, dr, string.Empty); + } } else { diff --git a/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlConnectionEditor.Designer.cs index 157f198e..6bfc6739 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.MySql/MySqlConnectionEditor.Designer.cs @@ -157,7 +157,7 @@ private void InitializeComponent() // PostgresConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.gbServer); this.Controls.Add(this.label1); this.Controls.Add(this.gbDatabase); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleConnectionEditor.Designer.cs index 3e58f834..ae389a32 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.OracleODPCore/OracleConnectionEditor.Designer.cs @@ -129,7 +129,7 @@ private void InitializeComponent() // OracleConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.gbServer); this.Controls.Add(this.label1); this.Controls.Add(this.btnAdvanced); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/FastReport.Data.Postgres.csproj b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/FastReport.Data.Postgres.csproj index 1c8fdba6..b0e1e36a 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/FastReport.Data.Postgres.csproj +++ b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/FastReport.Data.Postgres.csproj @@ -48,8 +48,6 @@ - - UserControl - + \ No newline at end of file diff --git a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresConnectionEditor.Designer.cs index d3ef8a76..8790b96e 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.Postgres/PostgresConnectionEditor.Designer.cs @@ -170,7 +170,7 @@ private void InitializeComponent() // PostgresConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.enableSystemSchemasCb); this.Controls.Add(this.gbServer); this.Controls.Add(this.label1); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/FastReport.Data.RavenDB.csproj b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/FastReport.Data.RavenDB.csproj index a71dc767..510305b8 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/FastReport.Data.RavenDB.csproj +++ b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/FastReport.Data.RavenDB.csproj @@ -19,7 +19,7 @@ FastReport.Data - + all @@ -41,9 +41,7 @@ - - UserControl - + RavenDBConnectionEditor.cs diff --git a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.Designer.cs index 7a9a49d2..1cbd6c9b 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.Designer.cs @@ -148,7 +148,7 @@ private void InitializeComponent() // RavenDBConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 19F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.gbDatabase); this.Controls.Add(this.gbServer); this.Controls.Add(this.labelLine1); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.cs b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.cs index 82537d10..5cae3056 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.RavenDB/RavenDBConnectionEditor.cs @@ -39,7 +39,7 @@ private void Localize() lblPassword.Text = res.Get("Password"); gbDatabase.Text = res.Get("Database"); lblDatabase.Text = res.Get("DatabaseName"); - tbCertificatePath.Image = Res.GetImage(1); + tbCertificatePath.ImageIndex = 1; res = new MyRes("Export,Email"); } diff --git a/Extras/Core/FastReport.Data/FastReport.Data.SQLite/SQLiteConnectionEditor.Designer.cs b/Extras/Core/FastReport.Data/FastReport.Data.SQLite/SQLiteConnectionEditor.Designer.cs index 71d7767f..21656408 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.SQLite/SQLiteConnectionEditor.Designer.cs +++ b/Extras/Core/FastReport.Data/FastReport.Data.SQLite/SQLiteConnectionEditor.Designer.cs @@ -97,7 +97,7 @@ private void InitializeComponent() // SQLiteConnectionEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.label1); this.Controls.Add(this.gbDatabase); this.Controls.Add(this.btnAdvanced); diff --git a/Extras/Core/FastReport.Data/FastReport.Data.sln b/Extras/Core/FastReport.Data/FastReport.Data.sln index f9e406ea..38637b90 100644 --- a/Extras/Core/FastReport.Data/FastReport.Data.sln +++ b/Extras/Core/FastReport.Data/FastReport.Data.sln @@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.Data.Couchbase", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.Data.RavenDB", "FastReport.Data.RavenDB\FastReport.Data.RavenDB.csproj", "{BB143926-05EB-4569-A474-EA21225EB6CC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.ClickHouse", "FastReport.ClickHouse\FastReport.ClickHouse\FastReport.ClickHouse.csproj", "{95AA6CC1-39AA-41A6-90E5-D8931DD814DF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -63,6 +65,10 @@ Global {BB143926-05EB-4569-A474-EA21225EB6CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {BB143926-05EB-4569-A474-EA21225EB6CC}.Release|Any CPU.ActiveCfg = Release|Any CPU {BB143926-05EB-4569-A474-EA21225EB6CC}.Release|Any CPU.Build.0 = Release|Any CPU + {95AA6CC1-39AA-41A6-90E5-D8931DD814DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95AA6CC1-39AA-41A6-90E5-D8931DD814DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95AA6CC1-39AA-41A6-90E5-D8931DD814DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95AA6CC1-39AA-41A6-90E5-D8931DD814DF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FastReport.Base/AssemblyInitializer.cs b/FastReport.Base/AssemblyInitializer.cs index a7715aa3..9dee0435 100644 --- a/FastReport.Base/AssemblyInitializer.cs +++ b/FastReport.Base/AssemblyInitializer.cs @@ -114,7 +114,7 @@ public AssemblyInitializer() RegisteredObjects.AddCategory("ReportPage,Barcodes", 123, 9, "Objects,BarcodeObject"); for (int i = 0; i <= Barcodes.Items.Length - 1; i++) - RegisteredObjects.Add(typeof(BarcodeObject), "ReportPage,Barcodes", -1, "ComponentMenu,Barcode,Barcodes,Barcode" + i, i); + RegisteredObjects.Add(typeof(BarcodeObject), "ReportPage,Barcodes", -1, Barcodes.Items[i].barcodeName, i); RegisteredObjects.Add(typeof(CheckBoxObject), "ReportPage", 124, 10); diff --git a/FastReport.Base/Base.cs b/FastReport.Base/Base.cs index 7605b616..07e13b8f 100644 --- a/FastReport.Base/Base.cs +++ b/FastReport.Base/Base.cs @@ -757,6 +757,7 @@ public virtual void Clear() ObjectCollection list = ChildObjects; foreach (Base c in list) { + c.Clear(); c.Dispose(); } } diff --git a/FastReport.Base/Data/CsvDataConnection.cs b/FastReport.Base/Data/CsvDataConnection.cs index 993917a5..93b2cdef 100644 --- a/FastReport.Base/Data/CsvDataConnection.cs +++ b/FastReport.Base/Data/CsvDataConnection.cs @@ -6,6 +6,7 @@ using System.Data.Common; using System.IO; using System.Net; +using FastReport.Utils; namespace FastReport.Data { @@ -297,6 +298,8 @@ protected override DataSet CreateDataSet() { string allText = ""; + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; WebRequest request; WebResponse response = null; try @@ -305,6 +308,8 @@ protected override DataSet CreateDataSet() if (uri.IsFile) { + if (Config.ForbidLocalData) + throw new Exception(Res.Get("ConnectionEditors,Common,OnlyUrlException")); request = (FileWebRequest)WebRequest.Create(uri); request.Timeout = 5000; response = (FileWebResponse)request.GetResponse(); diff --git a/FastReport.Base/Data/DataConnectionBase.cs b/FastReport.Base/Data/DataConnectionBase.cs index b4f9ab31..2783d679 100644 --- a/FastReport.Base/Data/DataConnectionBase.cs +++ b/FastReport.Base/Data/DataConnectionBase.cs @@ -4,6 +4,7 @@ using System.Data; using System.Data.Common; using System.Drawing.Design; +using FastReport.Data.JsonConnection; using FastReport.Utils; namespace FastReport.Data @@ -747,6 +748,48 @@ public virtual void DeleteTable(TableDataSource source) } } + /// + /// Clone table. + /// For internal use only. + /// + public virtual void Clone() + { + XmlItem item = new XmlItem(); + using(FRWriter writer = new FRWriter(item)) + { + writer.SerializeTo = SerializeTo.Clipboard; + writer.BlobStore = new BlobStore(false); + writer.Write(this); + } + using (FRReader reader = new FRReader(Report, item)) + { + reader.DeserializeFrom = SerializeTo.Clipboard; + reader.BlobStore = new BlobStore(false); + var connection = Activator.CreateInstance(this.GetType()) as DataConnectionBase; + connection.Parent = this.Parent; + connection.SetReport(Report); + reader.Read(connection); + connection.CreateUniqueName(); + foreach (TableDataSource table in connection.Tables) + table.CreateUniqueName(); + Report.Dictionary.AddChild(connection); + } + } + + protected void CreateUniqueNames(DataConnectionBase copyTo) + { + int i = 1; + string s; + do + { + s = this.Alias + i.ToString(); + i++; + } + while (Report.Dictionary.FindByAlias(s) != null); + copyTo.Alias = s; + copyTo.Name = s; + } + /// public override void Serialize(FRWriter writer) { diff --git a/FastReport.Base/Data/JsonConnection/JsonDataSourceConnection.cs b/FastReport.Base/Data/JsonConnection/JsonDataSourceConnection.cs index 21374a96..faba8532 100644 --- a/FastReport.Base/Data/JsonConnection/JsonDataSourceConnection.cs +++ b/FastReport.Base/Data/JsonConnection/JsonDataSourceConnection.cs @@ -201,6 +201,8 @@ private void InitConnection(bool rebuildSchema) // jsonText = client.DownloadString(jsonText); //} + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); HttpWebRequest req = (HttpWebRequest)WebRequest.Create(jsonText); foreach (var header in builder.Headers) diff --git a/FastReport.Base/Data/Total.cs b/FastReport.Base/Data/Total.cs index 1b535034..e919ac81 100644 --- a/FastReport.Base/Data/Total.cs +++ b/FastReport.Base/Data/Total.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Drawing.Design; @@ -6,390 +7,406 @@ namespace FastReport.Data { - /// - /// Specifies the total type. - /// - public enum TotalType - { /// - /// The total returns sum of values. + /// Specifies the total type. /// - Sum, + public enum TotalType + { + /// + /// The total returns sum of values. + /// + Sum, + + /// + /// The total returns minimal value. + /// + Min, + + /// + /// The total returns maximal value. + /// + Max, + + /// + /// The total returns average value. + /// + Avg, + + /// + /// The total returns number of values. + /// + Count, + + + /// + /// The total returns number of distinct values. + /// + CountDistinct + } /// - /// The total returns minimal value. + /// Represents a total that is used to calculate aggregates such as Sum, Min, Max, Avg, Count. /// - Min, + public partial class Total : Base + { + #region Fields + private TotalType totalType; + private string expression; + private DataBand evaluator; + private BandBase printOn; + private string evaluateCondition; + private bool includeInvisibleRows; + private bool resetAfterPrint; + private bool resetOnReprint; + // engine + private object value; + private int count; + private bool keeping; + private Total keepTotal; + private TotalCollection subTotals; + private TotalCollection parentTotal; + private const string subPrefix = "_sub"; + private Hashtable distinctValues; + #endregion + + #region Properties + /// + /// Gets or sets the total type. + /// + [DefaultValue(TotalType.Sum)] + [Category("Data")] + public TotalType TotalType + { + get { return totalType; } + set { totalType = value; } + } - /// - /// The total returns maximal value. - /// - Max, + /// + /// Gets or sets the expression used to calculate the total. + /// + [Category("Data")] + [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] + public string Expression + { + get { return expression; } + set { expression = value; } + } - /// - /// The total returns average value. - /// - Avg, + /// + /// Gets or sets the evaluator databand. + /// + /// + /// The total will be calculated for each row of this band. + /// + [Category("Data")] + public DataBand Evaluator + { + get { return evaluator; } + set { evaluator = value; } + } - /// - /// The total returns number of values. - /// - Count - } - - /// - /// Represents a total that is used to calculate aggregates such as Sum, Min, Max, Avg, Count. - /// - public partial class Total : Base - { - #region Fields - private TotalType totalType; - private string expression; - private DataBand evaluator; - private BandBase printOn; - private string evaluateCondition; - private bool includeInvisibleRows; - private bool resetAfterPrint; - private bool resetOnReprint; - // engine - private object value; - private int count; - private bool keeping; - private Total keepTotal; - private TotalCollection subTotals; - private TotalCollection parentTotal; - private const string subPrefix = "_sub"; - #endregion - - #region Properties - /// - /// Gets or sets the total type. - /// - [DefaultValue(TotalType.Sum)] - [Category("Data")] - public TotalType TotalType - { - get { return totalType; } - set { totalType = value; } - } + /// + /// This property is kept for compatibility only. + /// + [Category("Data")] + [Browsable(false)] + public BandBase Resetter + { + get { return printOn; } + set { printOn = value; } + } - /// - /// Gets or sets the expression used to calculate the total. - /// - [Category("Data")] - [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] - public string Expression - { - get { return expression; } - set { expression = value; } - } - - /// - /// Gets or sets the evaluator databand. - /// - /// - /// The total will be calculated for each row of this band. - /// - [Category("Data")] - public DataBand Evaluator - { - get { return evaluator; } - set { evaluator = value; } - } - - /// - /// This property is kept for compatibility only. - /// - [Category("Data")] - [Browsable(false)] - public BandBase Resetter - { - get { return printOn; } - set { printOn = value; } - } + /// + /// Gets or sets the band to print the total on. + /// + /// + /// The total will be resetted after the specified band has been printed. + /// + [Category("Data")] + public BandBase PrintOn + { + get { return printOn; } + set { printOn = value; } + } - /// - /// Gets or sets the band to print the total on. - /// - /// - /// The total will be resetted after the specified band has been printed. - /// - [Category("Data")] - public BandBase PrintOn - { - get { return printOn; } - set { printOn = value; } - } + /// + /// Gets or sets a value that determines whether the total should be resetted after print. + /// + [DefaultValue(true)] + [Category("Behavior")] + public bool ResetAfterPrint + { + get { return resetAfterPrint; } + set { resetAfterPrint = value; } + } - /// - /// Gets or sets a value that determines whether the total should be resetted after print. - /// - [DefaultValue(true)] - [Category("Behavior")] - public bool ResetAfterPrint - { - get { return resetAfterPrint; } - set { resetAfterPrint = value; } - } + /// + /// Gets or sets a value that determines whether the total should be resetted if printed + /// on repeated band (i.e. band with "RepeatOnEveryPage" flag). + /// + [DefaultValue(true)] + [Category("Behavior")] + public bool ResetOnReprint + { + get { return resetOnReprint; } + set { resetOnReprint = value; } + } - /// - /// Gets or sets a value that determines whether the total should be resetted if printed - /// on repeated band (i.e. band with "RepeatOnEveryPage" flag). - /// - [DefaultValue(true)] - [Category("Behavior")] - public bool ResetOnReprint - { - get { return resetOnReprint; } - set { resetOnReprint = value; } - } + /// + /// Gets or sets the condition which tells the total to evaluate. + /// + [Category("Data")] + [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] + public string EvaluateCondition + { + get { return evaluateCondition; } + set { evaluateCondition = value; } + } - /// - /// Gets or sets the condition which tells the total to evaluate. - /// - [Category("Data")] - [Editor("FastReport.TypeEditors.ExpressionEditor, FastReport", typeof(UITypeEditor))] - public string EvaluateCondition - { - get { return evaluateCondition; } - set { evaluateCondition = value; } - } - - /// - /// Gets or sets a value that determines if invisible rows of the Evaluator should - /// be included into the total's value. - /// - [DefaultValue(false)] - [Category("Behavior")] - public bool IncludeInvisibleRows - { - get { return includeInvisibleRows; } - set { includeInvisibleRows = value; } - } + /// + /// Gets or sets a value that determines if invisible rows of the Evaluator should + /// be included into the total's value. + /// + [DefaultValue(false)] + [Category("Behavior")] + public bool IncludeInvisibleRows + { + get { return includeInvisibleRows; } + set { includeInvisibleRows = value; } + } - /// - /// This property is not relevant to this class. - /// - [Browsable(false)] - public new Restrictions Restrictions - { - get { return base.Restrictions; } - set { base.Restrictions = value; } - } + /// + /// This property is not relevant to this class. + /// + [Browsable(false)] + public new Restrictions Restrictions + { + get { return base.Restrictions; } + set { base.Restrictions = value; } + } - /// - /// Gets the value of total. - /// - [Browsable(false)] - public object Value - { - get { return GetValue(); } - } + /// + /// Gets the value of total. + /// + [Browsable(false)] + public object Value + { + get { return GetValue(); } + } - private bool IsPageFooter - { - get - { - return PrintOn is PageFooterBand || PrintOn is ColumnFooterBand || - ((PrintOn is HeaderFooterBandBase) && (PrintOn as HeaderFooterBandBase).RepeatOnEveryPage); - } - } + private bool IsPageFooter + { + get + { + return PrintOn is PageFooterBand || PrintOn is ColumnFooterBand || + ((PrintOn is HeaderFooterBandBase) && (PrintOn as HeaderFooterBandBase).RepeatOnEveryPage); + } + } - private bool IsInsideHierarchy - { - get - { - return Report.Engine.HierarchyLevel > 1 && - !Name.StartsWith(subPrefix) && - PrintOn != null && PrintOn.ParentDataBand != null && PrintOn.ParentDataBand.IsHierarchical; - } - } - #endregion + private bool IsInsideHierarchy + { + get + { + return Report.Engine.HierarchyLevel > 1 && + !Name.StartsWith(subPrefix) && + PrintOn != null && PrintOn.ParentDataBand != null && PrintOn.ParentDataBand.IsHierarchical; + } + } + #endregion - #region Private Methods - private object GetValue() - { - if (IsInsideHierarchy) - { - Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); - return subTotal.Value; - } - - if (TotalType == TotalType.Avg) - { - if (value == null || count == 0) - return null; - return new Variant(value) / count; - } - else if (TotalType == TotalType.Count) - return count; - return value; - } + #region Private Methods + private object GetValue() + { + if (IsInsideHierarchy) + { + Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); + return subTotal.Value; + } - private void AddValue(object value) - { - if (value == null || value is DBNull) - return; - - if (this.value == null) - this.value = value; - else - { - switch (TotalType) + if (TotalType == TotalType.Avg) + { + if (value == null || count == 0) + return null; + return new Variant(value) / count; + } + else if (TotalType == TotalType.Count) + return count; + else if (TotalType == TotalType.CountDistinct) + return distinctValues.Keys.Count; + return value; + } + + private void AddValue(object value) { - case TotalType.Sum: - case TotalType.Avg: + if (value == null || value is DBNull) + return; + + if (TotalType == TotalType.CountDistinct) + { + distinctValues[value] = 1; + return; + } + + if (this.value == null) + this.value = value; + else + { + switch (TotalType) + { + case TotalType.Sum: + case TotalType.Avg: this.value = (new Variant(this.value) + new Variant(value)).Value; - break; + break; - case TotalType.Min: + case TotalType.Min: IComparable val1 = this.value as IComparable; IComparable val2 = value as IComparable; - if (val1 != null && val2 != null && val1.CompareTo(val2) > 0) + if (val1 != null && val2 != null && val1.CompareTo(val2) > 0) this.value = value; - break; + break; - case TotalType.Max: - val1 = this.value as IComparable; - val2 = value as IComparable; - if (val1 != null && val2 != null && val1.CompareTo(val2) < 0) + case TotalType.Max: + val1 = this.value as IComparable; + val2 = value as IComparable; + if (val1 != null && val2 != null && val1.CompareTo(val2) < 0) this.value = value; - break; + break; + } + } } - } - } - private Total FindSubTotal(string name) - { - Total result = subTotals.FindByName(name); - if (result == null) - { - result = this.Clone(); - result.Name = name; - subTotals.Add(result); - } - - return result; - } - #endregion - - #region Public Methods - /// - public override void Assign(Base source) - { - BaseAssign(source); - } + private Total FindSubTotal(string name) + { + Total result = subTotals.FindByName(name); + if (result == null) + { + result = this.Clone(); + result.Name = name; + subTotals.Add(result); + } - /// - public override void Serialize(FRWriter writer) - { - Total c = writer.DiffObject as Total; - base.Serialize(writer); - - if (TotalType != c.TotalType) - writer.WriteValue("TotalType", TotalType); - if (Expression != c.Expression) - writer.WriteStr("Expression", Expression); - if (Evaluator != c.Evaluator) - writer.WriteRef("Evaluator", Evaluator); - if (PrintOn != c.PrintOn) - writer.WriteRef("PrintOn", PrintOn); - if (ResetAfterPrint != c.ResetAfterPrint) - writer.WriteBool("ResetAfterPrint", ResetAfterPrint); - if (ResetOnReprint != c.ResetOnReprint) - writer.WriteBool("ResetOnReprint", ResetOnReprint); - if (EvaluateCondition != c.EvaluateCondition) - writer.WriteStr("EvaluateCondition", EvaluateCondition); - if (IncludeInvisibleRows != c.IncludeInvisibleRows) - writer.WriteBool("IncludeInvisibleRows", IncludeInvisibleRows); - } + return result; + } + #endregion - internal Total Clone() - { - Total total = new Total(); - total.SetReport(Report); - total.TotalType = TotalType; - total.Expression = Expression; - total.Evaluator = Evaluator; - total.PrintOn = PrintOn; - total.ResetAfterPrint = ResetAfterPrint; - total.ResetOnReprint = ResetOnReprint; - total.EvaluateCondition = EvaluateCondition; - total.IncludeInvisibleRows = IncludeInvisibleRows; - return total; - } - #endregion - - #region Report Engine - /// - public override string[] GetExpressions() - { - List expressions = new List(); - if (!String.IsNullOrEmpty(Expression)) - expressions.Add(Expression); - if (!String.IsNullOrEmpty(EvaluateCondition)) - expressions.Add(EvaluateCondition); - return expressions.ToArray(); - } + #region Public Methods + /// + public override void Assign(Base source) + { + BaseAssign(source); + } - /// - public override void Clear() - { - base.Clear(); - value = null; - count = 0; - } - - internal void AddValue() - { - if (IsInsideHierarchy) - { - Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); - subTotal.AddValue(); - return; - } - - if (!Evaluator.Visible && !IncludeInvisibleRows) - return; - if (!String.IsNullOrEmpty(EvaluateCondition) && (bool)Report.Calc(EvaluateCondition) == false) - return; - if (TotalType != TotalType.Count && String.IsNullOrEmpty(Expression)) - return; - - if (keeping) - { - keepTotal.AddValue(); - return; - } - - // if Total refers to another total - Total total = IsRefersToTotal(); - if (total != null) + /// + public override void Serialize(FRWriter writer) + { + Total c = writer.DiffObject as Total; + base.Serialize(writer); + + if (TotalType != c.TotalType) + writer.WriteValue("TotalType", TotalType); + if (Expression != c.Expression) + writer.WriteStr("Expression", Expression); + if (Evaluator != c.Evaluator) + writer.WriteRef("Evaluator", Evaluator); + if (PrintOn != c.PrintOn) + writer.WriteRef("PrintOn", PrintOn); + if (ResetAfterPrint != c.ResetAfterPrint) + writer.WriteBool("ResetAfterPrint", ResetAfterPrint); + if (ResetOnReprint != c.ResetOnReprint) + writer.WriteBool("ResetOnReprint", ResetOnReprint); + if (EvaluateCondition != c.EvaluateCondition) + writer.WriteStr("EvaluateCondition", EvaluateCondition); + if (IncludeInvisibleRows != c.IncludeInvisibleRows) + writer.WriteBool("IncludeInvisibleRows", IncludeInvisibleRows); + } + + internal Total Clone() + { + Total total = new Total(); + total.SetReport(Report); + total.TotalType = TotalType; + total.Expression = Expression; + total.Evaluator = Evaluator; + total.PrintOn = PrintOn; + total.ResetAfterPrint = ResetAfterPrint; + total.ResetOnReprint = ResetOnReprint; + total.EvaluateCondition = EvaluateCondition; + total.IncludeInvisibleRows = IncludeInvisibleRows; + return total; + } + #endregion + + #region Report Engine + /// + public override string[] GetExpressions() { - if (!total.parentTotal.Contains(this)) + List expressions = new List(); + if (!String.IsNullOrEmpty(Expression)) + expressions.Add(Expression); + if (!String.IsNullOrEmpty(EvaluateCondition)) + expressions.Add(EvaluateCondition); + return expressions.ToArray(); + } + + /// + public override void Clear() + { + base.Clear(); + value = null; + count = 0; + distinctValues.Clear(); + } + + internal void AddValue() + { + if (IsInsideHierarchy) + { + Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); + subTotal.AddValue(); + return; + } + + if (!Evaluator.Visible && !IncludeInvisibleRows) + return; + if (!String.IsNullOrEmpty(EvaluateCondition) && (bool)Report.Calc(EvaluateCondition) == false) + return; + if (TotalType != TotalType.Count && String.IsNullOrEmpty(Expression)) + return; + + if (keeping) + { + keepTotal.AddValue(); + return; + } + + // if Total refers to another total + Total total = IsRefersToTotal(); + if (total != null) { - total.parentTotal.Add(this); + if (!total.parentTotal.Contains(this)) + { + total.parentTotal.Add(this); + } + return; } - return; + + object value = TotalType == TotalType.Count ? null : Report.Calc(Expression); + AddValue(value); + if (TotalType != TotalType.Avg || (value != null && !(value is DBNull))) + count++; } - object value = TotalType == TotalType.Count ? null : Report.Calc(Expression); - AddValue(value); - if (TotalType != TotalType.Avg || (value != null && !(value is DBNull))) - count++; - } + internal void ResetValue() + { + if (IsInsideHierarchy) + { + Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); + subTotal.ResetValue(); + return; + } - internal void ResetValue() - { - if (IsInsideHierarchy) - { - Total subTotal = FindSubTotal(subPrefix + Report.Engine.HierarchyLevel.ToString()); - subTotal.ResetValue(); - return; - } - - Clear(); - } + Clear(); + } internal void ExecuteTotal(object val) { @@ -413,37 +430,48 @@ private Total IsRefersToTotal() return Report.Dictionary.Totals.FindByName(expr); } - internal void StartKeep() - { - if (!IsPageFooter || keeping) - return; - keeping = true; + internal void StartKeep() + { + if (!IsPageFooter || keeping) + return; + keeping = true; - keepTotal = Clone(); - } + keepTotal = Clone(); + } - internal void EndKeep() - { - if (!IsPageFooter || !keeping) - return; - keeping = false; + internal void EndKeep() + { + if (!IsPageFooter || !keeping) + return; + keeping = false; - AddValue(keepTotal.value); - count += keepTotal.count; - } - #endregion + if (TotalType == TotalType.CountDistinct) + { + foreach (object key in keepTotal.distinctValues) + { + distinctValues[key] = 1; + } + } + else + { + AddValue(keepTotal.value); + count += keepTotal.count; + } + } + #endregion - /// - /// Initializes a new instance of the class with default settings. - /// - public Total() - { - expression = ""; - evaluateCondition = ""; - resetAfterPrint = true; - subTotals = new TotalCollection(null); + /// + /// Initializes a new instance of the class with default settings. + /// + public Total() + { + expression = ""; + evaluateCondition = ""; + resetAfterPrint = true; + subTotals = new TotalCollection(null); parentTotal = new TotalCollection(null); - SetFlags(Flags.CanCopy, false); + distinctValues = new Hashtable(); + SetFlags(Flags.CanCopy, false); + } } - } } diff --git a/FastReport.Base/Data/XmlDataConnection.cs b/FastReport.Base/Data/XmlDataConnection.cs index 8bb63fd6..9da9827f 100644 --- a/FastReport.Base/Data/XmlDataConnection.cs +++ b/FastReport.Base/Data/XmlDataConnection.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using FastReport.Utils; using System.Data.Common; +using System.Net; namespace FastReport.Data { @@ -60,16 +61,15 @@ public string XmlFile ConnectionString = builder.ToString(); } } - #endregion + #endregion #region Protected Methods /// protected override DataSet CreateDataSet() { DataSet dataset = base.CreateDataSet(); - if (!String.IsNullOrEmpty(XsdFile)) - dataset.ReadXmlSchema(XsdFile); - dataset.ReadXml(XmlFile); + ReadXmlSchema(dataset); + ReadXml(dataset); return dataset; } @@ -117,12 +117,94 @@ public override string QuoteIdentifier(string value, DbConnection connection) { return value; } - #endregion + #endregion - /// - /// Initializes a new instance of the class with default settings. - /// - public XmlDataConnection() + #region private methods + private void ReadXml(DataSet dataset) + { + try + { + Uri uri = new Uri(XmlFile); + + if (uri.IsFile) + { + if (Config.ForbidLocalData) + throw new Exception(Res.Get("ConnectionEditors,Common,OnlyUrlException")); + dataset.ReadXml(XmlFile); + } + else if (uri.OriginalString.StartsWith("http") || uri.OriginalString.StartsWith("ftp")) + { + LoadXmlFromUrl(dataset); + } + } + catch (Exception e) + { + throw e; + } + } + + private void LoadXmlFromUrl(DataSet dataset) + { + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(XmlFile); + using (var response = req.GetResponse() as HttpWebResponse) + { + var encoding = response.CharacterSet.Equals(String.Empty) ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet); + + using (var responseStream = response.GetResponseStream()) + using (var reader = new System.IO.StreamReader(responseStream, encoding)) + dataset.ReadXml(reader, XmlReadMode.Auto); + } + } + + private void ReadXmlSchema(DataSet dataset) + { + if (String.IsNullOrEmpty(XsdFile)) + return; + + try + { + Uri uri = new Uri(XsdFile); + + if (uri.IsFile) + { + if (Config.ForbidLocalData) + throw new Exception(Res.Get("ConnectionEditors,Common,OnlyUrlException")); + dataset.ReadXmlSchema(XsdFile); + } + else if (uri.OriginalString.StartsWith("http") || uri.OriginalString.StartsWith("ftp")) + { + LoadXmlSchemaFromUrl(dataset); + } + } + catch (Exception e) + { + throw e; + } + } + + private void LoadXmlSchemaFromUrl(DataSet dataset) + { + ServicePointManager.Expect100Continue = true; + ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); + HttpWebRequest req = (HttpWebRequest)WebRequest.Create(XsdFile); + using (var response = req.GetResponse() as HttpWebResponse) + { + var encoding = response.CharacterSet.Equals(String.Empty) ? Encoding.UTF8 : Encoding.GetEncoding(response.CharacterSet); + + using (var responseStream = response.GetResponseStream()) + using (var reader = new System.IO.StreamReader(responseStream, encoding)) + dataset.ReadXmlSchema(reader); + } + } + + #endregion + + /// + /// Initializes a new instance of the class with default settings. + /// + public XmlDataConnection() { IsSqlBased = false; } diff --git a/FastReport.Base/Engine/ReportEngine.Bands.cs b/FastReport.Base/Engine/ReportEngine.Bands.cs index efc0ffcb..c06c52b8 100644 --- a/FastReport.Base/Engine/ReportEngine.Bands.cs +++ b/FastReport.Base/Engine/ReportEngine.Bands.cs @@ -375,7 +375,7 @@ internal void AddToPreparedPages(BandBase band) { bandHeight = 0; } - while (FreeSpace - bandHeight - band.Child.Height > 0) + while (FreeSpace - bandHeight - band.Child.Height >= 0) { float saveCurY = CurY; ShowBand(band.Child); diff --git a/FastReport.Base/Engine/ReportEngine.cs b/FastReport.Base/Engine/ReportEngine.cs index 9da041ab..813ed196 100644 --- a/FastReport.Base/Engine/ReportEngine.cs +++ b/FastReport.Base/Engine/ReportEngine.cs @@ -344,16 +344,6 @@ private void InitializeSecondPassData() } } - private void InitializePages() - { - for (int i = 0; i < Report.Pages.Count; i++) - { - ReportPage page = Report.Pages[i] as ReportPage; - if (page != null) - PreparedPages.AddSourcePage(page); - } - } - private void PrepareToFirstPass(bool append) { finalPass = !Report.DoublePass; diff --git a/FastReport.Base/Export/ExportBase.cs b/FastReport.Base/Export/ExportBase.cs index dadf991e..15b8e28a 100644 --- a/FastReport.Base/Export/ExportBase.cs +++ b/FastReport.Base/Export/ExportBase.cs @@ -465,37 +465,41 @@ public void Export(Report report, Stream stream) internal void ExportPageNew(int pageNo) { PreparedPage ppage = Report.PreparedPages.GetPreparedPage(pageNo); - ReportPage page = null; - try { - page = ppage.StartGetPage(pageNo); - page.Width = ppage.PageSize.Width; - page.Height = ppage.PageSize.Height; - ExportPageBegin(page); - float topShift = 0; - foreach (Base obj in ppage.GetPageItems(page, false)) + ReportPage page = null; + try { - if (shiftNonExportable && topShift != 0 && obj is BandBase && - !(obj is PageFooterBand) && !(obj as BandBase).PrintOnBottom) - { - (obj as BandBase).Top -= topShift; - } - if ((obj as BandBase).Exportable - || webPreview) - ExportBand(obj); - else if (obj != null) + page = ppage.StartGetPage(pageNo); + page.Width = ppage.PageSize.Width; + page.Height = ppage.PageSize.Height; + ExportPageBegin(page); + float topShift = 0; + foreach (Base obj in ppage.GetPageItems(page, false)) { - if (shiftNonExportable) - topShift += (obj as BandBase).Height; - obj.Dispose(); - } + if (shiftNonExportable && topShift != 0 && obj is BandBase && + !(obj is PageFooterBand) && !(obj as BandBase).PrintOnBottom) + { + (obj as BandBase).Top -= topShift; + } + if ((obj as BandBase).Exportable + || webPreview) + ExportBand(obj); + else if (obj != null) + { + if (shiftNonExportable) + topShift += (obj as BandBase).Height; + obj.Dispose(); + } + } + ExportPageEnd(page); } - ExportPageEnd(page); - } - finally - { - ppage.EndGetPage(page); + finally + { + ppage.EndGetPage(page); + } + if(page != null) + page.Dispose(); } } diff --git a/FastReport.Base/Export/ExportUtils.cs b/FastReport.Base/Export/ExportUtils.cs index 5ba7365c..df420ffc 100644 --- a/FastReport.Base/Export/ExportUtils.cs +++ b/FastReport.Base/Export/ExportUtils.cs @@ -539,16 +539,6 @@ internal static string GetID() return SystemFake.Guid.NewGuid().ToString(); } - internal static void CopyStream(Stream source, Stream target) - { - source.Position = 0; - int bufflength = 2048; - byte[] buff = new byte[bufflength]; - int i; - while ((i = source.Read(buff, 0, bufflength)) > 0) - target.Write(buff, 0, i); - } - internal static byte[] StringToByteArray(string source) { byte[] result = new byte[source.Length]; diff --git a/FastReport.Base/Export/Image/ImageExport.cs b/FastReport.Base/Export/Image/ImageExport.cs index 1831c9f4..cb9d8b78 100644 --- a/FastReport.Base/Export/Image/ImageExport.cs +++ b/FastReport.Base/Export/Image/ImageExport.cs @@ -473,7 +473,7 @@ protected override void Start() bigImage = CreateImage((int)(w * ResolutionX / 96f), (int)(h * ResolutionY / 96f), ""); bigGraphics = Graphics.FromImage(bigImage); - bigGraphics.Clear(Color.White); + bigGraphics.Clear(Color.Transparent); } pageNumber = 0; } @@ -503,8 +503,14 @@ protected override void ExportPageBegin(ReportPage page) else g = Graphics.FromImage(image); - state = g.Save(); - g.FillRegion(Brushes.White, new Region(new RectangleF(0, curOriginY, width, height))); + state = g.Save(); + + g.FillRegion(Brushes.Transparent, new Region(new RectangleF(0, curOriginY, width, height))); + if (bigImage != null && curOriginY + height * 2 > bigImage.Height) + page.Fill.Draw(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache), new RectangleF(0, curOriginY, widthK, bigImage.Height - curOriginY)); + else + page.Fill.Draw(new FRPaintEventArgs(g, 1, 1, Report.GraphicCache), new RectangleF(0, curOriginY, widthK, height + paddingNonSeparatePages * 2)); + if (image == bigImage) { diff --git a/FastReport.Base/Fills.cs b/FastReport.Base/Fills.cs index 0a6a0c35..7e4677aa 100644 --- a/FastReport.Base/Fills.cs +++ b/FastReport.Base/Fills.cs @@ -1119,7 +1119,8 @@ public TextureFill() Stream dummy = ResourceLoader.GetStream("FastReport", "icon16.ico"); using (MemoryStream ms = new MemoryStream()) { - Res.CopyTo(dummy, ms); + const int BUFFER_SIZE = 4 * 1024; + dummy.CopyTo(ms, BUFFER_SIZE); SetImageData(ms.ToArray()); } WrapMode = WrapMode.Tile; diff --git a/FastReport.Base/Functions/NumToWordsPl.cs b/FastReport.Base/Functions/NumToWordsPl.cs new file mode 100644 index 00000000..ba120dda --- /dev/null +++ b/FastReport.Base/Functions/NumToWordsPl.cs @@ -0,0 +1,128 @@ +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace FastReport.Functions +{ + internal class NumToWordsPl : NumToWordsBase + { + private static Dictionary currencyList; + + private static string[] fixedWords = + { + "", "jeden", "dwa", "trzy", "cztery", "pięć", "sześć", + "siedem", "osiem", "dziewięć", "dziesięć", "jedenaście", + "dwanaście", "trzynaście", "czternaście", "piętnaście", + "szesnaście", "siedemnaście", "osiemnaście", "dziewiętnaście" + }; + + private static string[] tens = + { + "", "dziesięć", "dwadzieścia", "trzydzieści", "czterdzieści", "pięćdziesiąt", + "sześćdziesiąt", "siedemdziesiąt", "osiemdziesiąt", "dziewięćdziesiąt" + }; + + private static string[] hunds = + { + "", "sto", "dwieście", "trzysta", "czterysta", + "czterysta", "sześćset", "siedemset", "osiemset", "dziewięćset" + }; + + private static WordInfo thousands = new WordInfo(false, "tysiąc", "tysiące", "tysięcy"); + private static WordInfo millions = new WordInfo(true, "milion", "miliony", "milionów"); + private static WordInfo milliards = new WordInfo(true, "miliard", "miliardy", "miliardów"); + private static WordInfo trillions = new WordInfo(true, "bilion", "biliony", "bilionów"); + + protected override string GetFixedWords(bool male, long value) + { + string result = fixedWords[value]; + if (!male) + { + if (value == 1) + return "jedna"; + if (value == 2) + return "dwie"; + } + return result; + } + + protected override string GetTen(bool male, long value) + { + return tens[value]; + } + + protected override string GetHund(bool male, long value) + { + return hunds[value / 100]; + } + + protected override WordInfo GetThousands() + { + return thousands; + } + + protected override WordInfo GetMillions() + { + return millions; + } + + protected override WordInfo GetMilliards() + { + return milliards; + } + + protected override WordInfo GetTrillions() + { + return trillions; + } + + protected override CurrencyInfo GetCurrency(string currencyName) + { + currencyName = currencyName.ToUpper(); + if (currencyName == "RUR") + currencyName = "RUB"; + return currencyList[currencyName]; + } + + protected override string GetZero() + { + return "zero"; + } + + protected override string GetMinus() + { + return "minus"; + } + + protected override string Case(long value, WordInfo info) + { + value = value % 100; + if (value > GetFixedWordsCount()) + value = value % 10; + + switch (value) + { + case 1: + return info.one; + + case 2: + case 3: + case 4: + return info.two; + + default: + return info.many; + } + } + + static NumToWordsPl() + { + currencyList = new Dictionary(1); + currencyList.Add("PLN", new CurrencyInfo( + new WordInfo(true, "złoty", "zlote", "złotych"), + new WordInfo(false, "grosz", "grosze", "groszy"))); + + } + } +} \ No newline at end of file diff --git a/FastReport.Base/Functions/StdFunctions.cs b/FastReport.Base/Functions/StdFunctions.cs index 45bf2235..b6461e8b 100644 --- a/FastReport.Base/Functions/StdFunctions.cs +++ b/FastReport.Base/Functions/StdFunctions.cs @@ -1052,6 +1052,41 @@ public static string ToWordsPersian(object value, string one, string many) return new NumToWordsPersian().ConvertNumber(Convert.ToDecimal(value), true, one, many, many); } + /// + /// Converts a numeric value to a polish string representation of that value. + /// + /// The numeric value to convert. + /// The string representation of the specified value. + public static string ToWordsPl(object value) + { + return ToWordsPl(value, "PLN"); + } + + /// + /// Converts a numeric value to a polish representation of that value. + /// + /// he numeric value to convert. + /// The 3-digit ISO name of the currency, for example "EUR". + /// + public static string ToWordsPl(object value, string currencyName) + { + return new NumToWordsPl().ConvertCurrency(Convert.ToDecimal(value), currencyName); + } + + /// + /// Converts a numeric value to a polish string representation of that value. + /// + /// The numeric value to convert. + /// True if the name is of male gender. + /// The name in singular form, for example "silla". + /// The name in plural form, for example "Sillas". + /// The name in plural form, for example "Sillas". + /// The string representation of the specified value. + public static string ToWordsPl(object value, string one, string many) + { + return new NumToWordsPl().ConvertNumber(Convert.ToDecimal(value), true, one, many, many); + } + /// /// Converts a value to an english (US) alphabet string representation of that value. /// @@ -1319,6 +1354,9 @@ internal static void Register() RegisteredObjects.AddFunction(myConv.GetMethod("ToLetters", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToLetters"); RegisteredObjects.AddFunction(myConv.GetMethod("ToLettersRu", new Type[] { typeof(object) }), "Conversion,ToLettersRu"); RegisteredObjects.AddFunction(myConv.GetMethod("ToLettersRu", new Type[] { typeof(object), typeof(bool) }), "Conversion,ToLettersRu"); + RegisteredObjects.AddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string) }), "Conversion,ToWordsPl"); + RegisteredObjects.AddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object) }), "Conversion,ToWordsPl"); + RegisteredObjects.AddFunction(myConv.GetMethod("ToWordsPl", new Type[] { typeof(object), typeof(string), typeof(string) }), "Conversion,ToWordsPl"); #endregion #region Program Flow diff --git a/FastReport.Base/Matrix/MatrixCellDescriptor.cs b/FastReport.Base/Matrix/MatrixCellDescriptor.cs index 9e71ef7a..491508dc 100644 --- a/FastReport.Base/Matrix/MatrixCellDescriptor.cs +++ b/FastReport.Base/Matrix/MatrixCellDescriptor.cs @@ -41,11 +41,16 @@ public enum MatrixAggregateFunction /// Count, - /// - /// Specifies the custom function. - /// - Custom - } + /// + /// Specifies the count of distinct values. + /// + CountDistinct, + + /// + /// Specifies the custom function. + /// + Custom + } /// /// Determines how matrix percents are calculated. diff --git a/FastReport.Base/Matrix/MatrixHelper.cs b/FastReport.Base/Matrix/MatrixHelper.cs index 9cf9f6c8..206192bb 100644 --- a/FastReport.Base/Matrix/MatrixHelper.cs +++ b/FastReport.Base/Matrix/MatrixHelper.cs @@ -911,6 +911,16 @@ private object GetAggregatedValue(ArrayList list, int cellIndex) if (function == MatrixAggregateFunction.Count) return list.Count; + if (function == MatrixAggregateFunction.CountDistinct) + { + Hashtable distinctValues = new Hashtable(); + foreach (object value in list) + { + distinctValues[value] = 1; + } + return distinctValues.Keys.Count; + } + // aggregated value Variant aggrValue = new Variant(); diff --git a/FastReport.Base/Matrix/MatrixObject.cs b/FastReport.Base/Matrix/MatrixObject.cs index 4d7e61da..412d4dca 100644 --- a/FastReport.Base/Matrix/MatrixObject.cs +++ b/FastReport.Base/Matrix/MatrixObject.cs @@ -649,6 +649,7 @@ public override void Assign(Base source) MatrixEvenStylePriority = src.MatrixEvenStylePriority; SplitRows = src.SplitRows; PrintIfEmpty = src.PrintIfEmpty; + data = src.Data; } /// diff --git a/FastReport.Base/PictureObject.cs b/FastReport.Base/PictureObject.cs index d45c0d0d..736a508a 100644 --- a/FastReport.Base/PictureObject.cs +++ b/FastReport.Base/PictureObject.cs @@ -376,6 +376,8 @@ public override void DrawImage(FRPaintEventArgs e) GraphicsState state = g.Save(); try { + if (Config.IsRunningOnMono) // strange behavior of mono - we need to reset clip before we set new one + g.ResetClip(); g.SetClip(drawRect); Report report = Report; if (report != null && report.SmoothGraphics) @@ -419,7 +421,7 @@ public override void DrawImage(FRPaintEventArgs e) protected override void DrawImageInternal2(Graphics graphics, PointF upperLeft, PointF upperRight, PointF lowerLeft) { - Image image = transparentImage != null ? transparentImage : Image; + Image image = transparentImage != null ? transparentImage.Clone() as Image : Image.Clone() as Image; if (image == null) return; if (Grayscale) @@ -435,7 +437,24 @@ protected override void DrawImageInternal2(Graphics graphics, PointF upperLeft, image = grayscaleBitmap; } - graphics.DrawImage(image, new PointF[] { upperLeft, upperRight, lowerLeft }); + //graphics.DrawImage(image, new PointF[] { upperLeft, upperRight, lowerLeft }); + DrawImage3Points(graphics, image, upperLeft, upperRight, lowerLeft); + image = null; + } + + // This is analogue of graphics.DrawImage(image, PointF[] points) method. + // The original gdi+ method does not work properly in mono on linux/macos. + private void DrawImage3Points(Graphics g, Image image, PointF p0, PointF p1, PointF p2) + { + if (image == null || image.Width == 0 || image.Height == 0) + return; + RectangleF rect = new RectangleF(0, 0, image.Width, image.Height); + float m11 = (p1.X - p0.X) / rect.Width; + float m12 = (p1.Y - p0.Y) / rect.Width; + float m21 = (p2.X - p0.X) / rect.Height; + float m22 = (p2.Y - p0.Y) / rect.Height; + g.MultiplyTransform(new System.Drawing.Drawing2D.Matrix(m11, m12, m21, m22, p0.X, p0.Y), MatrixOrder.Prepend); + g.DrawImage(image, rect); } /// diff --git a/FastReport.Base/PictureObjectBase.cs b/FastReport.Base/PictureObjectBase.cs index 7ac3257e..15c89296 100644 --- a/FastReport.Base/PictureObjectBase.cs +++ b/FastReport.Base/PictureObjectBase.cs @@ -787,7 +787,6 @@ public override void Serialize(FRWriter writer) /// /// /// -#if MONO internal virtual void DrawImageInternal(FRPaintEventArgs e, RectangleF drawRect) { bool rotate = Angle == 90 || Angle == 270; @@ -799,105 +798,8 @@ internal virtual void DrawImageInternal(FRPaintEventArgs e, RectangleF drawRect) PointF lowerLeft; System.Drawing.Drawing2D.Matrix matrix = e.Graphics.Transform; GetImageAngleTransform(drawRect, imageWidth, imageHeight, e.ScaleX, e.ScaleY, matrix.OffsetX, matrix.OffsetY, out upperLeft, out upperRight, out lowerLeft); - - // TODO translate tranform matrix, WTF mono or coreCompat or both - // cant work with negative transforms so need to fix it - - bool NeedTransform = Config.IsRunningOnMono; - System.Drawing.Drawing2D.Matrix matrixBack = null; -#if NETSTANDARD2_0 || NETSTANDARD2_1 - NeedTransform = true; -#else - NeedTransform = Config.IsRunningOnMono; -#endif - if (NeedTransform) - { - matrixBack = e.Graphics.Transform; - System.Drawing.Drawing2D.Matrix matrixTemp = new System.Drawing.Drawing2D.Matrix( - matrixBack.Elements[0], - matrixBack.Elements[1], - matrixBack.Elements[2], - matrixBack.Elements[3], - 0, - 0 - ); - - upperLeft.X += matrixBack.OffsetX; - upperLeft.Y += matrixBack.OffsetY; - - upperRight.X += matrixBack.OffsetX; - upperRight.Y += matrixBack.OffsetY; - - lowerLeft.X += matrixBack.OffsetX; - lowerLeft.Y += matrixBack.OffsetY; - - e.Graphics.Transform = matrixTemp; - } - - DrawImageInternal2(e.Graphics, upperLeft, upperRight, lowerLeft); - - if (NeedTransform) - e.Graphics.Transform = matrixBack; - } - -#else - internal virtual void DrawImageInternal(FRPaintEventArgs e, RectangleF drawRect) - { - bool rotate = Angle == 90 || Angle == 270; - float imageWidth = ImageWidth;//rotate ? Image.Height : Image.Width; - float imageHeight = ImageHeight;//rotate ? Image.Width : Image.Height; - - PointF upperLeft; - PointF upperRight; - PointF lowerLeft; - System.Drawing.Drawing2D.Matrix matrix = e.Graphics.Transform; - GetImageAngleTransform(drawRect, imageWidth, imageHeight, e.ScaleX, e.ScaleY, matrix.OffsetX, matrix.OffsetY, out upperLeft, out upperRight, out lowerLeft); - - // TODO translate tranform matrix, WTF mono or coreCompat or both - // cant work with negative transforms so need to fix it - -#if NETSTANDARD2_0 || NETSTANDARD2_1 - - System.Drawing.Drawing2D.Matrix matrixBack = e.Graphics.Transform; - /* - Временное решение (т.к. не удалось найти универсального), которое устраняет проблемы с экспортом картинок в Excel2007 на Windows, но на Linux не работает. - */ - if (!Config.IsWindows) - { - System.Drawing.Drawing2D.Matrix matrixTemp = new System.Drawing.Drawing2D.Matrix( - matrixBack.Elements[0], - matrixBack.Elements[1], - matrixBack.Elements[2], - matrixBack.Elements[3], - 0, - 0 - ); - - upperLeft.X += matrixBack.OffsetX; - upperLeft.Y += matrixBack.OffsetY; - - upperRight.X += matrixBack.OffsetX; - upperRight.Y += matrixBack.OffsetY; - - lowerLeft.X += matrixBack.OffsetX; - lowerLeft.Y += matrixBack.OffsetY; - - e.Graphics.Transform = matrixTemp; - } -#endif - DrawImageInternal2(e.Graphics, upperLeft, upperRight, lowerLeft); - -#if NETSTANDARD2_0 || NETSTANDARD2_1 - if (!Config.IsWindows) - { - e.Graphics.Transform = matrixBack; - } -#endif - } -#endif - #endregion Internal Methods #region Protected Methods diff --git a/FastReport.Base/Preview/PreparedPage.cs b/FastReport.Base/Preview/PreparedPage.cs index 1fc5e132..96a6e639 100644 --- a/FastReport.Base/Preview/PreparedPage.cs +++ b/FastReport.Base/Preview/PreparedPage.cs @@ -99,7 +99,6 @@ private bool DoAdd(Base c, XmlItem item) DoAdd(obj, item); } } - return true; } @@ -279,52 +278,54 @@ internal void ReCalcSizes() reader.DeserializeFrom = SerializeTo.Preview; reader.BlobStore = preparedPages.BlobStore; reader.ReadChildren = false; - ReportPage page; - Base obj; - BandBase band; - page = ReadPage(Report, item, false, reader); - float maxWidth = 0.0f; - float maxHeight = 0.0f; - for (int i = 0; i < item.Count; i++) + using (ReportPage page = ReadPage(Report, item, false, reader)) { - obj = ReadObject(page, item[i], true, reader); - if (obj is BandBase) + if (page.UnlimitedHeight | page.UnlimitedWidth) { - band = obj as BandBase; - float bandsHeight = band.Top + band.Height; - if (maxHeight < bandsHeight) - maxHeight = bandsHeight; - float bandWidth = 0.0f; - foreach (ComponentBase comp in band.Objects) + + float maxWidth = 0.0f; + float maxHeight = 0.0f; + for (int i = 0; i < item.Count; i++) { - if ((comp.Anchor & AnchorStyles.Right) == 0 && comp.Dock == DockStyle.None) + using (Base obj = ReadObject(page, item[i], true, reader)) { - bandWidth = Math.Max(bandWidth, comp.Left + comp.Width); + if (obj is BandBase) + { + BandBase band = obj as BandBase; + float bandsHeight = band.Top + band.Height; + if (maxHeight < bandsHeight) + maxHeight = bandsHeight; + float bandWidth = 0.0f; + foreach (ComponentBase comp in band.Objects) + { + if ((comp.Anchor & AnchorStyles.Right) == 0 && comp.Dock == DockStyle.None) + { + bandWidth = Math.Max(bandWidth, comp.Left + comp.Width); + } + } + if (maxWidth < bandWidth) + maxWidth = bandWidth; + } } } - if (maxWidth < bandWidth) - maxWidth = bandWidth; - } - obj.Dispose(); - } - if (page.UnlimitedHeight) - page.UnlimitedHeightValue = maxHeight + (page.TopMargin + page.BottomMargin) * Units.Millimeters; - if (page.UnlimitedWidth) - page.UnlimitedWidthValue = maxWidth + (page.LeftMargin + page.RightMargin) * Units.Millimeters; + if (page.UnlimitedHeight) + page.UnlimitedHeightValue = maxHeight + (page.TopMargin + page.BottomMargin) * Units.Millimeters; + if (page.UnlimitedWidth) + page.UnlimitedWidthValue = maxWidth + (page.LeftMargin + page.RightMargin) * Units.Millimeters; - pageSize = new SizeF(page.WidthInPixels, page.HeightInPixels); + } + pageSize = new SizeF(page.WidthInPixels, page.HeightInPixels); - using (FRWriter writer = new FRWriter(item)) - { - writer.SerializeTo = SerializeTo.Preview; - writer.SaveChildren = false; - writer.BlobStore = preparedPages.BlobStore; - writer.Write(page); + using (FRWriter writer = new FRWriter(item)) + { + writer.SerializeTo = SerializeTo.Preview; + writer.SaveChildren = false; + writer.BlobStore = preparedPages.BlobStore; + writer.Write(page); + } } - - page.Dispose(); } } diff --git a/FastReport.Base/Preview/PreparedPages.cs b/FastReport.Base/Preview/PreparedPages.cs index 05b8f260..94ca8817 100644 --- a/FastReport.Base/Preview/PreparedPages.cs +++ b/FastReport.Base/Preview/PreparedPages.cs @@ -525,17 +525,18 @@ public void Load(Stream stream) { Clear(); + if (stream.Length == 0) + return; + if (!stream.CanSeek) { MemoryStream tempStream = new MemoryStream(); - FileUtils.CopyStream(stream, tempStream); + const int BUFFER_SIZE = 32768; + stream.CopyTo(tempStream, BUFFER_SIZE); tempStream.Position = 0; stream = tempStream; } - if (stream.Length == 0) - return; - bool compressed = Compressor.IsStreamCompressed(stream); if (compressed) stream = Compressor.Decompress(stream, false); diff --git a/FastReport.Base/Preview/SourcePages.cs b/FastReport.Base/Preview/SourcePages.cs index 0aa83c5d..a3c40532 100644 --- a/FastReport.Base/Preview/SourcePages.cs +++ b/FastReport.Base/Preview/SourcePages.cs @@ -5,11 +5,11 @@ namespace FastReport.Preview { - internal class SourcePages : IDisposable + internal partial class SourcePages : IDisposable { #region Fields - private List pages; - private PreparedPages preparedPages; + private readonly List pages; + private readonly PreparedPages preparedPages; #endregion #region Properties @@ -25,6 +25,7 @@ public ReportPage this[int index] #endregion #region Private Methods + private Base CloneObjects(Base source, Base parent) { if (source is ReportComponentBase && !(source as ReportComponentBase).FlagPreviewVisible) @@ -36,11 +37,7 @@ private Base CloneObjects(Base source, Base parent) Base clone = Activator.CreateInstance(source.GetType()) as Base; using (XmlItem xml = new XmlItem()) using (FRWriter writer = new FRWriter(xml)) -#if MONO - using (FRReader reader = new FRReader(source.Report, xml)) -#else using (FRReader reader = new FRReader(null, xml)) -#endif { reader.DeserializeFrom = SerializeTo.SourcePages; writer.SaveChildren = false; @@ -71,12 +68,9 @@ private Base CloneObjects(Base source, Base parent) ObjectCollection childObjects = source.ChildObjects; foreach (Base c in childObjects) { - CloneObjects(c, clone); + CloneObjects(c, clone); } clone.Parent = parent; -#if MONO - clone.SetReport(source.Report); -#endif return clone; } #endregion diff --git a/FastReport.Base/TextObject.cs b/FastReport.Base/TextObject.cs index 6a285952..c6f98bfd 100644 --- a/FastReport.Base/TextObject.cs +++ b/FastReport.Base/TextObject.cs @@ -525,7 +525,7 @@ public float LineHeight /// [DefaultValue(0f)] [Category("Appearance")] - [TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")] + //[TypeConverter("FastReport.TypeConverters.UnitsConverter, FastReport")] public float FirstTabOffset { get { return firstTabOffset; } @@ -1078,17 +1078,17 @@ internal HtmlTextRenderer GetHtmlTextRenderer(Graphics g, RectangleF textRect, f internal HtmlTextRenderer GetHtmlTextRenderer(Graphics g, float scale, float fontScale, RectangleF textRect, StringFormat format) { - return GetHtmlTextRenderer(g, fontScale, scale, fontScale, textRect, format); + return GetHtmlTextRenderer(g, fontScale, scale, fontScale, textRect, format, false); } - internal HtmlTextRenderer GetHtmlTextRenderer(Graphics g, float formatScale, float scale, float fontScale, RectangleF textRect, StringFormat format) + internal HtmlTextRenderer GetHtmlTextRenderer(Graphics g, float formatScale, float scale, float fontScale, RectangleF textRect, StringFormat format, bool isPrinting) { return new HtmlTextRenderer(Text, g, font.Name, font.Size, font.Style, TextColor, textOutline.Color, textRect, Underlines, format, horzAlign, vertAlign, ParagraphFormat.MultipleScale(formatScale), ForceJustify, - scale * 96f / DrawUtils.ScreenDpi, fontScale * 96f / DrawUtils.ScreenDpi, InlineImageCache - ); + scale * 96f / DrawUtils.ScreenDpi, fontScale * 96f / DrawUtils.ScreenDpi, InlineImageCache, + isPrinting); } /// @@ -1137,7 +1137,7 @@ public void DrawText(FRPaintEventArgs e) try { using (HtmlTextRenderer htmlRenderer = GetHtmlTextRenderer(e.Graphics, e.ScaleX, - IsPrinting ? 1 : e.ScaleX, IsPrinting ? 1 : e.ScaleX, textRect, format)) + IsPrinting ? 1 : e.ScaleX, IsPrinting ? 1 : e.ScaleX, textRect, format, IsPrinting)) { htmlRenderer.Draw(); } @@ -1156,7 +1156,7 @@ public void DrawText(FRPaintEventArgs e) outlinePen, textRect, format, HorzAlign, VertAlign, LineHeight * e.ScaleY, Angle, FontWidthRatio, ForceJustify, Wysiwyg, HasHtmlTags, false, e.ScaleX * 96f / DrawUtils.ScreenDpi, - IsPrinting ? 1 : e.ScaleX * 96f / DrawUtils.ScreenDpi, InlineImageCache); + IsPrinting ? 1 : e.ScaleX * 96f / DrawUtils.ScreenDpi, InlineImageCache, IsPrinting); advancedRenderer.Draw(); } else diff --git a/FastReport.Base/Utils/Compressor.cs b/FastReport.Base/Utils/Compressor.cs index ee068d1f..de55a2ae 100644 --- a/FastReport.Base/Utils/Compressor.cs +++ b/FastReport.Base/Utils/Compressor.cs @@ -11,11 +11,7 @@ internal static class Compressor { public static Stream Decompress(Stream source, bool bidiStream) { - int byte1 = source.ReadByte(); - int byte2 = source.ReadByte(); - source.Position -= 2; - bool result = byte1 == 0x1F && byte2 == 0x8B; - if (result) + if (IsStreamCompressed(source)) { if (bidiStream) { @@ -23,13 +19,8 @@ public static Stream Decompress(Stream source, bool bidiStream) Stream stream = new MemoryStream(); using (GZipStream gzip = new GZipStream(source, CompressionMode.Decompress)) { - byte[] buffer = new byte[4096]; - while (true) - { - int bytesRead = gzip.Read(buffer, 0, 4096); - if (bytesRead == 0) break; - stream.Write(buffer, 0, bytesRead); - } + const int BUFFER_SIZE = 4096; + gzip.CopyTo(stream, BUFFER_SIZE); } stream.Position = 0; return stream; diff --git a/FastReport.Base/Utils/Config.cs b/FastReport.Base/Utils/Config.cs index fac12630..6419fe5b 100644 --- a/FastReport.Base/Utils/Config.cs +++ b/FastReport.Base/Utils/Config.cs @@ -25,9 +25,10 @@ public static partial class Config #endif #region Private Fields - private static CultureInfo engCultureInfo = new CultureInfo("en-US"); - private static XmlDocument FDoc = new XmlDocument(); + private static readonly CultureInfo engCultureInfo = new CultureInfo("en-US"); + private static readonly XmlDocument FDoc = new XmlDocument(); + private static readonly string version = typeof(Report).Assembly.GetName().Version.ToString(3); private static string FFolder = null; private static string FFontListFolder = null; private static string FLogs = ""; @@ -36,13 +37,16 @@ public static partial class Config private static bool FRightToLeft = false; private static string FTempFolder = null; private static string systemTempFolder = null; - private static bool FStringOptimization = false; + private static bool FStringOptimization = true; private static bool FWebMode; private static bool preparedCompressed = true; private static bool disableHotkeys = false; private static bool disableBacklight = false; private static bool enableScriptSecurity = false; private static ScriptSecurityProperties scriptSecurityProps = null; + private static bool forbidLocalData = false; + private static bool userSetsScriptSecurity = false; + #if NETSTANDARD2_0 || NETSTANDARD2_1 private static readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); @@ -67,6 +71,17 @@ public static bool IsWindows #endif + + /// + /// Gets or sets a value indicating is it impossible to specify a local data path in Xml and Csv. + /// + public static bool ForbidLocalData + { + get { return forbidLocalData; } + set { forbidLocalData = value; } + } + + /// /// Gets or sets the optimization of strings. Is experimental feature. /// @@ -188,7 +203,7 @@ public static string SystemTempFolder /// public static string Version { - get { return typeof(Report).Assembly.GetName().Version.ToString(3); } + get { return version; } } /// @@ -218,6 +233,13 @@ public static bool EnableScriptSecurity if (OnEnableScriptSecurityChanged != null) OnEnableScriptSecurityChanged.Invoke(null, null); enableScriptSecurity = value; + // + userSetsScriptSecurity = true; + if (value) + { + if(scriptSecurityProps == null) + scriptSecurityProps = new ScriptSecurityProperties(); + } } } @@ -233,7 +255,6 @@ public static ScriptSecurityProperties ScriptSecurityProps { get { return scriptSecurityProps; } } - #endregion Public Properties #region Internal Methods @@ -254,12 +275,14 @@ internal static void Init() { FIsRunningOnMono = Type.GetType("Mono.Runtime") != null; - InitWebMode(); + CheckWebMode(); + #if !NETSTANDARD if (!WebMode) LoadConfig(); #endif - if (WebMode) + + if (!userSetsScriptSecurity && WebMode) { enableScriptSecurity = true; // don't throw event scriptSecurityProps = new ScriptSecurityProperties(); @@ -281,23 +304,28 @@ internal static void Init() } } - private static void InitWebMode() + private static void CheckWebMode() { - string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; - -#if NETSTANDARD - ConfigSetsWebMode(String.Compare(processName, "dotnet") == 0); + // If we/user sets 'WebMode = true' before this check - Config shouln't change it (because check may be incorrect) + if (!WebMode) + { +#if NETSTANDARD || NETCOREAPP + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var loadedAsmbly in loadedAssemblies) + { + bool isAspNetCore = loadedAsmbly.GetName().Name.StartsWith("Microsoft.AspNetCore"); + if (isAspNetCore) + { + WebMode = true; + break; + } + } #else - ConfigSetsWebMode(String.Compare(processName, "iisexpress") == 0 || - String.Compare(processName, "w3wp") == 0); + string processName = System.Diagnostics.Process.GetCurrentProcess().ProcessName; + WebMode = String.Compare(processName, "iisexpress") == 0 || + String.Compare(processName, "w3wp") == 0; #endif - } - - // If we/user sets 'WebMode = true' before checks in Config.cs - Config shouln't change it (because check may be incorrect) - private static void ConfigSetsWebMode(bool value) - { - if (!FWebMode) - WebMode = value; + } } internal static void WriteLogString(string s) @@ -581,8 +609,6 @@ private static void RestoreUIOptions() } } - - #endregion Private Methods } } \ No newline at end of file diff --git a/FastReport.Base/Utils/DrawUtils.cs b/FastReport.Base/Utils/DrawUtils.cs index 88452b7c..6bfe419e 100644 --- a/FastReport.Base/Utils/DrawUtils.cs +++ b/FastReport.Base/Utils/DrawUtils.cs @@ -1,9 +1,15 @@ using System; using System.Drawing; -using System.Windows.Forms; namespace FastReport.Utils { + internal enum MonoRendering + { + Undefined, + Pango, + Cairo + } + public static partial class DrawUtils { private static Font FDefaultFont; @@ -13,6 +19,7 @@ public static partial class DrawUtils private static Font FFixedFont; private static int FScreenDpi; private static float FDpiFX; + private static MonoRendering FMonoRendering = MonoRendering.Undefined; public static int ScreenDpi { @@ -197,6 +204,22 @@ public static void FloodFill(Bitmap bmp, int x, int y, Color color, Color replac FloodFill(bmp, x, y + 1, color, replacementColor); } + internal static MonoRendering GetMonoRendering(Graphics printerGraphics) + { + if (FMonoRendering == MonoRendering.Undefined) + { + GraphicsUnit savedUnit = printerGraphics.PageUnit; + printerGraphics.PageUnit = GraphicsUnit.Point; + + string s = "test string test string test string test string"; + float f1 = printerGraphics.MeasureString(s, DefaultReportFont).Width; + FMonoRendering = f1 > 200 ? MonoRendering.Pango : MonoRendering.Cairo; + + printerGraphics.PageUnit = savedUnit; + } + return FMonoRendering; + } + } } \ No newline at end of file diff --git a/FastReport.Base/Utils/FastNameCreator.cs b/FastReport.Base/Utils/FastNameCreator.cs index 3dcbe5b0..fb0b89a2 100644 --- a/FastReport.Base/Utils/FastNameCreator.cs +++ b/FastReport.Base/Utils/FastNameCreator.cs @@ -77,7 +77,7 @@ public FastNameCreator(ObjectCollection objects) } baseNames[baseName] = num; } - else + else if (!baseNames.ContainsKey(objName)) { baseNames[objName] = 0; } diff --git a/FastReport.Base/Utils/FileUtils.cs b/FastReport.Base/Utils/FileUtils.cs index 4b7115bc..917133f6 100644 --- a/FastReport.Base/Utils/FileUtils.cs +++ b/FastReport.Base/Utils/FileUtils.cs @@ -35,13 +35,5 @@ public static string GetRelativePath(string absPath, string baseDirectoryPath) result += String.Join(Path.DirectorySeparatorChar.ToString(), aPath, indx, aPath.Length - indx); return result; } - - public static void CopyStream(Stream input, Stream output) - { - byte[] buffer = new byte[32768]; - int read; - while ((read = input.Read(buffer, 0, buffer.Length)) > 0) - output.Write(buffer, 0, read); - } } } diff --git a/FastReport.Base/Utils/HtmlTextRenderer.cs b/FastReport.Base/Utils/HtmlTextRenderer.cs index 39b9685b..48db751b 100644 --- a/FastReport.Base/Utils/HtmlTextRenderer.cs +++ b/FastReport.Base/Utils/HtmlTextRenderer.cs @@ -40,7 +40,7 @@ internal class HtmlTextRenderer : IDisposable private StyleDescriptor initalStyle; private float fontScale; private FastString cacheString = new FastString(100); - + private bool isPrinting; #endregion Private Fields #region Public Properties @@ -110,7 +110,7 @@ public bool WordWrap public HtmlTextRenderer(string text, Graphics g, string font, float size, FontStyle style, Color color, Color underlineColor, RectangleF rect, bool underlines, StringFormat format, HorzAlign horzAlign, VertAlign vertAlign, - ParagraphFormat paragraphFormat, bool forceJustify, float scale, float fontScale, InlineImageCache cache) + ParagraphFormat paragraphFormat, bool forceJustify, float scale, float fontScale, InlineImageCache cache, bool isPrinting = false) { this.cache = cache; this.scale = scale; @@ -142,6 +142,7 @@ public HtmlTextRenderer(string text, Graphics g, string font, float size, this.paragraphFormat = paragraphFormat; this.font = font; this.size = size; + this.isPrinting = isPrinting; everUnderlines = underlines; backgrounds = new List(); diff --git a/FastReport.Base/Utils/ImageHelper.cs b/FastReport.Base/Utils/ImageHelper.cs index 248cad93..2f65e577 100644 --- a/FastReport.Base/Utils/ImageHelper.cs +++ b/FastReport.Base/Utils/ImageHelper.cs @@ -164,8 +164,7 @@ public static byte[] LoadURL(string url) { if (!String.IsNullOrEmpty(url)) { - //Enable TLS1.2, 1.1. and 1.0; - System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); // System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12; + System.Net.ServicePointManager.SecurityProtocol = (SecurityProtocolType)(0xc0 | 0x300 | 0xc00); using (WebClient web = new WebClient()) { return web.DownloadData(url); diff --git a/FastReport.Base/Utils/Res.cs b/FastReport.Base/Utils/Res.cs index b327676d..c058c623 100644 --- a/FastReport.Base/Utils/Res.cs +++ b/FastReport.Base/Utils/Res.cs @@ -88,17 +88,6 @@ private static void LoadBuiltinLocale() FLocaleLoaded = true; } - public static void CopyTo(Stream input, Stream output) - { - byte[] buffer = new byte[4 * 1024]; - int bytesRead; - - while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) - { - output.Write(buffer, 0, bytesRead); - } - } - /// /// Loads the locale from a file. /// diff --git a/FastReport.Base/Utils/ResourceLoader.cs b/FastReport.Base/Utils/ResourceLoader.cs index 26df9756..90906190 100644 --- a/FastReport.Base/Utils/ResourceLoader.cs +++ b/FastReport.Base/Utils/ResourceLoader.cs @@ -58,15 +58,10 @@ public static Stream UnpackStream(string assembly, string resource) using (Stream gzipStream = new GZipStream(packedStream, CompressionMode.Decompress, true)) { MemoryStream result = new MemoryStream(); - - byte[] buffer = new byte[4096]; - while (true) - { - int bytesRead = gzipStream.Read(buffer, 0, 4096); - if (bytesRead == 0) break; - result.Write(buffer, 0, bytesRead); - } - + + const int BUFFER_SIZE = 4096; + gzipStream.CopyTo(result, BUFFER_SIZE); + result.Position = 0; return result; } diff --git a/FastReport.Base/Utils/TextRenderer.cs b/FastReport.Base/Utils/TextRenderer.cs index 30a64d84..f6d7f392 100644 --- a/FastReport.Base/Utils/TextRenderer.cs +++ b/FastReport.Base/Utils/TextRenderer.cs @@ -9,2643 +9,2637 @@ namespace FastReport.Utils { - /// - /// Advanced text renderer is used to perform the following tasks: - /// - draw justified text, text with custom line height, text containing html tags; - /// - calculate text height, get part of text that does not fit in the display rectangle; - /// - get paragraphs, lines, words and char sequence to perform accurate export to such - /// formats as PDF, TXT, RTF - /// - /// Here is how one may operate the renderer items: - /// - /// foreach (AdvancedTextRenderer.Paragraph paragraph in renderer.Paragraphs) - /// { - /// foreach (AdvancedTextRenderer.Line line in paragraph.Lines) - /// { - /// foreach (AdvancedTextRenderer.Word word in line.Words) - /// { - /// if (renderer.HtmlTags) - /// { - /// foreach (AdvancedTextRenderer.Run run in word.Runs) - /// { - /// using (Font f = run.GetFont()) - /// using (Brush b = run.GetBrush()) - /// { - /// g.DrawString(run.Text, f, b, run.Left, run.Top, renderer.Format); - /// } - /// } - /// } - /// else - /// { - /// g.DrawString(word.Text, renderer.Font, renderer.Brush, word.Left, word.Top, renderer.Format); - /// } - /// } - /// } - /// } - /// - /// - public class AdvancedTextRenderer - { - #region Fields - private List paragraphs; - private string text; - private Graphics graphics; - private Font font; - private Brush brush; - private Pen outlinePen; - private RectangleF displayRect; - private StringFormat format; - private HorzAlign horzAlign; - private VertAlign vertAlign; - private float lineHeight; - private float fontLineHeight; - private int angle; - private float widthRatio; - private bool forceJustify; - private bool wysiwyg; - private bool htmlTags; - private bool pDFMode; - private float spaceWidth; - private float scale; - private InlineImageCache cache; - private float fontScale; - #endregion - - #region Properties - public List Paragraphs + /// + /// Advanced text renderer is used to perform the following tasks: + /// - draw justified text, text with custom line height, text containing html tags; + /// - calculate text height, get part of text that does not fit in the display rectangle; + /// - get paragraphs, lines, words and char sequence to perform accurate export to such + /// formats as PDF, TXT, RTF + /// + /// Here is how one may operate the renderer items: + /// + /// foreach (AdvancedTextRenderer.Paragraph paragraph in renderer.Paragraphs) + /// { + /// foreach (AdvancedTextRenderer.Line line in paragraph.Lines) + /// { + /// foreach (AdvancedTextRenderer.Word word in line.Words) + /// { + /// if (renderer.HtmlTags) + /// { + /// foreach (AdvancedTextRenderer.Run run in word.Runs) + /// { + /// using (Font f = run.GetFont()) + /// using (Brush b = run.GetBrush()) + /// { + /// g.DrawString(run.Text, f, b, run.Left, run.Top, renderer.Format); + /// } + /// } + /// } + /// else + /// { + /// g.DrawString(word.Text, renderer.Font, renderer.Brush, word.Left, word.Top, renderer.Format); + /// } + /// } + /// } + /// } + /// + /// + public class AdvancedTextRenderer { - get { return paragraphs; } - } + #region Fields + private List paragraphs; + private string text; + private Graphics graphics; + private Font font; + private Brush brush; + private Pen outlinePen; + private RectangleF displayRect; + private StringFormat format; + private HorzAlign horzAlign; + private VertAlign vertAlign; + private float lineHeight; + private float fontLineHeight; + private int angle; + private float widthRatio; + private bool forceJustify; + private bool wysiwyg; + private bool htmlTags; + private bool pDFMode; + private float spaceWidth; + private float scale; + private InlineImageCache cache; + private float fontScale; + #endregion + + #region Properties + public List Paragraphs + { + get { return paragraphs; } + } - public Graphics Graphics - { - get { return graphics; } - } + public Graphics Graphics + { + get { return graphics; } + } - public Font Font - { - get { return font; } - } + public Font Font + { + get { return font; } + } - public Brush Brush - { - get { return brush; } - } + public Brush Brush + { + get { return brush; } + } - public Pen OutlinePen - { - get { return outlinePen; } - } + public Pen OutlinePen + { + get { return outlinePen; } + } - public Color BrushColor - { - get { return brush is SolidBrush ? (brush as SolidBrush).Color : Color.Black; } - } + public Color BrushColor + { + get { return brush is SolidBrush ? (brush as SolidBrush).Color : Color.Black; } + } - public RectangleF DisplayRect - { - get { return displayRect; } - } + public RectangleF DisplayRect + { + get { return displayRect; } + } - public StringFormat Format - { - get { return format; } - } + public StringFormat Format + { + get { return format; } + } - public HorzAlign HorzAlign - { - get { return horzAlign; } - } + public HorzAlign HorzAlign + { + get { return horzAlign; } + } - public VertAlign VertAlign - { - get { return vertAlign; } - } + public VertAlign VertAlign + { + get { return vertAlign; } + } - public float LineHeight - { - get { return lineHeight; } - } + public float LineHeight + { + get { return lineHeight; } + } - public float FontLineHeight - { - get { return fontLineHeight; } - } + public float FontLineHeight + { + get { return fontLineHeight; } + } - public int Angle - { - get { return angle; } - } + public int Angle + { + get { return angle; } + } - public float WidthRatio - { - get { return widthRatio; } - } + public float WidthRatio + { + get { return widthRatio; } + } - public bool ForceJustify - { - get { return forceJustify; } - } + public bool ForceJustify + { + get { return forceJustify; } + } - public bool Wysiwyg - { - get { return wysiwyg; } - } + public bool Wysiwyg + { + get { return wysiwyg; } + } - public bool HtmlTags - { - get { return htmlTags; } - } + public bool HtmlTags + { + get { return htmlTags; } + } - public float TabSize - { - get - { - // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0] - float firstTab = 0; - float[] tabSizes = Format.GetTabStops(out firstTab); - if (tabSizes.Length > 1) - return tabSizes[1]; - return 0; - } - } + public float TabSize + { + get + { + // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0] + float firstTab = 0; + float[] tabSizes = Format.GetTabStops(out firstTab); + if (tabSizes.Length > 1) + return tabSizes[1]; + return 0; + } + } - public float TabOffset - { - get - { - // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0] - float firstTab = 0; - float[] tabSizes = Format.GetTabStops(out firstTab); - if (tabSizes.Length > 0) - return tabSizes[0]; - return 0; - } - } + public float TabOffset + { + get + { + // re fix tab offset #2823 sorry linux users, on linux firstTab is firstTab not tabSizes[0] + float firstTab = 0; + float[] tabSizes = Format.GetTabStops(out firstTab); + if (tabSizes.Length > 0) + return tabSizes[0]; + return 0; + } + } - public bool WordWrap - { - get { return (Format.FormatFlags & StringFormatFlags.NoWrap) == 0; } - } + public bool WordWrap + { + get { return (Format.FormatFlags & StringFormatFlags.NoWrap) == 0; } + } - public bool RightToLeft - { - get { return (Format.FormatFlags & StringFormatFlags.DirectionRightToLeft) != 0; } - } - - public bool PDFMode - { - get { return pDFMode; } - } - - internal float SpaceWidth - { - get { return spaceWidth; } - } + public bool RightToLeft + { + get { return (Format.FormatFlags & StringFormatFlags.DirectionRightToLeft) != 0; } + } - /// - /// The scale for font tag - /// - public float FontScale { get { return fontScale; } set { fontScale = value; } } - public float Scale { get { return scale; } set { scale = value; } } + public bool PDFMode + { + get { return pDFMode; } + } - public InlineImageCache Cache - { - get - { - if (cache == null) - cache = new InlineImageCache(); - return cache; - } - } - #endregion + internal float SpaceWidth + { + get { return spaceWidth; } + } - #region Private Methods + /// + /// The scale for font tag + /// + public float FontScale { get { return fontScale; } set { fontScale = value; } } + public float Scale { get { return scale; } set { scale = value; } } - const string ab = "abcdefabcdef"; - const string a40b = "abcdef abcdef"; + public InlineImageCache Cache + { + get + { + if (cache == null) + cache = new InlineImageCache(); + return cache; + } + } + #endregion - internal static float CalculateSpaceSize(Graphics g, Font f) - { - float w_ab = g.MeasureString(ab, f).Width; - float w_a40b = g.MeasureString(a40b, f).Width; - return (w_a40b - w_ab) / 40; - } - private void SplitToParagraphs(string text) - { - StyleDescriptor style = new StyleDescriptor(Font.Style, BrushColor, BaseLine.Normal); - if (HtmlTags) - text = text.Replace("
", "\r\n").Replace("
", "\r\n").Replace("
", "\r\n"); - string[] lines = text.Split(new char[] { '\n' }); - int originalCharIndex = 0; - - foreach (string line in lines) - { - string s = line; - if (s.Length > 0 && s[s.Length - 1] == '\r') - s = s.Remove(s.Length - 1); - - Paragraph paragraph = new Paragraph(s, this, originalCharIndex); - paragraphs.Add(paragraph); - if (HtmlTags) - style = paragraph.WrapHtmlLines(style); - else - paragraph.WrapLines(); - - originalCharIndex += line.Length + 1; - } - - // skip empty paragraphs at the end - for (int i = paragraphs.Count - 1; i >= 0; i--) - { - if (paragraphs[i].IsEmpty && paragraphs.Count != 1) - paragraphs.RemoveAt(i); - else - break; - } - } + #region Private Methods + + const string ab = "abcdefabcdef"; + const string a40b = "abcdef abcdef"; + + internal static float CalculateSpaceSize(Graphics g, Font f) + { + float w_ab = g.MeasureString(ab, f).Width; + float w_a40b = g.MeasureString(a40b, f).Width; + return (w_a40b - w_ab) / 40; + } + + private void SplitToParagraphs(string text) + { + StyleDescriptor style = new StyleDescriptor(Font.Style, BrushColor, BaseLine.Normal); + if (HtmlTags) + text = text.Replace("
", "\r\n").Replace("
", "\r\n").Replace("
", "\r\n"); + string[] lines = text.Split(new char[] { '\n' }); + int originalCharIndex = 0; + + foreach (string line in lines) + { + string s = line; + if (s.Length > 0 && s[s.Length - 1] == '\r') + s = s.Remove(s.Length - 1); + + Paragraph paragraph = new Paragraph(s, this, originalCharIndex); + paragraphs.Add(paragraph); + if (HtmlTags) + style = paragraph.WrapHtmlLines(style); + else + paragraph.WrapLines(); + + originalCharIndex += line.Length + 1; + } + + // skip empty paragraphs at the end + for (int i = paragraphs.Count - 1; i >= 0; i--) + { + if (paragraphs[i].IsEmpty && paragraphs.Count != 1) + paragraphs.RemoveAt(i); + else + break; + } + } + + private void AdjustParagraphLines() + { + // calculate text height + float height = 0; + height = CalcHeight(); + /*foreach (Paragraph paragraph in Paragraphs) + { + height += paragraph.Lines.Count * LineHeight; + }*/ + + // calculate Y offset + float offsetY = DisplayRect.Top; + if (VertAlign == VertAlign.Center) + offsetY += (DisplayRect.Height - height) / 2; + else if (VertAlign == VertAlign.Bottom) + offsetY += (DisplayRect.Height - height) - 1; + + for (int i = 0; i < Paragraphs.Count; i++) + { + Paragraph paragraph = Paragraphs[i]; + paragraph.AlignLines(i == Paragraphs.Count - 1 && ForceJustify); + + // adjust line tops + foreach (Line line in paragraph.Lines) + { + line.Top = offsetY; + line.MakeUnderlines(); + offsetY += line.CalcHeight(); + } + } + } + #endregion + + #region Public Methods + public void Draw() + { + // set clipping + GraphicsState state = Graphics.Save(); + Graphics.SetClip(DisplayRect, CombineMode.Intersect); + + // reset alignment + StringAlignment saveAlign = Format.Alignment; + StringAlignment saveLineAlign = Format.LineAlignment; + Format.Alignment = StringAlignment.Near; + Format.LineAlignment = StringAlignment.Near; + + if (Angle != 0) + { + Graphics.TranslateTransform(DisplayRect.Left + DisplayRect.Width / 2, + DisplayRect.Top + DisplayRect.Height / 2); + Graphics.RotateTransform(Angle); + } + + Graphics.ScaleTransform(WidthRatio, 1); + + foreach (Paragraph paragraph in Paragraphs) + { + paragraph.Draw(); + } + + // restore alignment and clipping + Format.Alignment = saveAlign; + Format.LineAlignment = saveLineAlign; + Graphics.Restore(state); + } + + public float CalcHeight() + { + int charsFit = 0; + StyleDescriptor style = null; + return CalcHeight(out charsFit, out style); + } + + public float CalcHeight(out int charsFit, out StyleDescriptor style) + { + charsFit = 0; + style = null; + float height = 0; + float displayHeight = DisplayRect.Height; + if (LineHeight > displayHeight) + return 0; + + foreach (Paragraph paragraph in Paragraphs) + { + foreach (Line line in paragraph.Lines) + { + height += line.CalcHeight(); + if (charsFit == 0 && height > displayHeight) + { + charsFit = line.OriginalCharIndex; + if (HtmlTags) + style = line.Style; + } + } + } + + if (charsFit == 0) + charsFit = text.Length; + return height; + } + + public float CalcWidth() + { + float width = 0; + + foreach (Paragraph paragraph in Paragraphs) + { + foreach (Line line in paragraph.Lines) + { + if (width < line.Width) + width = line.Width; + } + } + return width + spaceWidth; + } + + internal float GetTabPosition(float pos) + { + float tabOffset = TabOffset; + float tabSize = TabSize; + int tabPosition = (int)((pos - tabOffset) / tabSize); + if (pos < tabOffset) + return tabOffset; + return (tabPosition + 1) * tabSize + tabOffset; + } + #endregion + + public AdvancedTextRenderer(string text, Graphics g, Font font, Brush brush, Pen outlinePen, + RectangleF rect, StringFormat format, HorzAlign horzAlign, VertAlign vertAlign, + float lineHeight, int angle, float widthRatio, + bool forceJustify, bool wysiwyg, bool htmlTags, bool pdfMode, + float scale, float fontScale, InlineImageCache cache, bool isPrinting = false) + { + this.cache = cache; + this.scale = scale; + this.fontScale = fontScale; + paragraphs = new List(); + this.text = text; + graphics = g; + this.font = font; + this.brush = brush; + this.outlinePen = outlinePen; + displayRect = rect; + this.format = format; + this.horzAlign = horzAlign; + this.vertAlign = vertAlign; + this.lineHeight = lineHeight; + fontLineHeight = font.GetHeight(g); + if (this.lineHeight == 0) + { + this.lineHeight = fontLineHeight; + if (isPrinting && Config.IsRunningOnMono && DrawUtils.GetMonoRendering(g) == MonoRendering.Pango) + { + // we need this in order to fix inconsistent line spacing when print using Pango rendering + this.lineHeight = fontLineHeight * 1.33f; + } + } + this.angle = angle % 360; + this.widthRatio = widthRatio; + this.forceJustify = forceJustify; + this.wysiwyg = wysiwyg; + this.htmlTags = htmlTags; + pDFMode = pdfMode; + spaceWidth = CalculateSpaceSize(g, font);// g.MeasureString(" ", font).Width; + + StringFormatFlags saveFlags = Format.FormatFlags; + StringTrimming saveTrimming = Format.Trimming; + + // match DrawString behavior: + // if height is less than 1.25 of font height, turn off word wrap + // commented out due to bug with band.break + //if (rect.Height < FFontLineHeight * 1.25f) + //FFormat.FormatFlags |= StringFormatFlags.NoWrap; + + // if word wrap is set, ignore trimming + if (WordWrap) + Format.Trimming = StringTrimming.Word; + + Format.FormatFlags = Format.FormatFlags | StringFormatFlags.MeasureTrailingSpaces; + + if (Angle != 0) + { + // shift displayrect + displayRect.X = -DisplayRect.Width / 2; + displayRect.Y = -DisplayRect.Height / 2; + + // rotate displayrect if angle is 90 or 270 + if ((Angle >= 90 && Angle < 180) || (Angle >= 270 && Angle < 360)) + displayRect = new RectangleF(DisplayRect.Y, DisplayRect.X, DisplayRect.Height, DisplayRect.Width); + } + + displayRect.X /= WidthRatio; + displayRect.Width /= WidthRatio; + + SplitToParagraphs(text); + AdjustParagraphLines(); + + // restore original values + displayRect = rect; + Format.FormatFlags = saveFlags; + Format.Trimming = saveTrimming; + } + + + /// + /// Paragraph represents single paragraph. It consists of one or several . + /// + public class Paragraph + { + #region Fields + private List lines; + private AdvancedTextRenderer renderer; + private string text; + private int originalCharIndex; + #endregion + + #region Properties + public List Lines + { + get { return lines; } + } + + public AdvancedTextRenderer Renderer + { + get { return renderer; } + } + + public bool Last + { + get { return renderer.Paragraphs[renderer.Paragraphs.Count - 1] == this; } + } + + public bool IsEmpty + { + get { return String.IsNullOrEmpty(text); } + } + + public string Text + { + get { return text; } + } + #endregion + + #region Private Methods + private int MeasureString(string text) + { + if (text.Length > 0) + { + // BEGIN: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b + float left = 0; + int tabFit = 0; + while (text.Length > 0 && text[0] == '\t') + { + left = Renderer.GetTabPosition(left); + text = text.Substring(1); + if (Renderer.DisplayRect.Width < left) + return tabFit; + tabFit++; + } + if (tabFit > 0 && Renderer.DisplayRect.Width < left) + return tabFit; + int charsFit = 0; + int linesFit = 0; + // END: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b + Renderer.Graphics.MeasureString(text, Renderer.Font, + new SizeF(Renderer.DisplayRect.Width - left, Renderer.FontLineHeight + 1), + Renderer.Format, out charsFit, out linesFit); + return charsFit + tabFit; + } + return 0; + } + #endregion + + #region Public Methods + public void WrapLines() + { + string text = this.text; + int charsFit = 0; + + if (String.IsNullOrEmpty(text)) + { + lines.Add(new Line("", this, originalCharIndex)); + return; + } + + if (Renderer.WordWrap) + { + int originalCharIndex = this.originalCharIndex; + while (text.Length > 0) + { + charsFit = MeasureString(text); + + // avoid infinite loop when width of object less than width of one character + if (charsFit == 0) + { + break; + } + + string textFit = text.Substring(0, charsFit).TrimEnd(new char[] { ' ' }); + lines.Add(new Line(textFit, this, originalCharIndex)); + text = text.Substring(charsFit) + // Fix for linux system + .TrimStart(' '); + originalCharIndex += charsFit; + } + } + else + { + string ellipsis = "\u2026"; + StringTrimming trimming = Renderer.Format.Trimming; + if (trimming == StringTrimming.EllipsisPath) + Renderer.Format.Trimming = StringTrimming.Character; + charsFit = MeasureString(text); + + switch (trimming) + { + case StringTrimming.Character: + case StringTrimming.Word: + text = text.Substring(0, charsFit); + break; + + case StringTrimming.EllipsisCharacter: + case StringTrimming.EllipsisWord: + if (charsFit < text.Length) + { + text = text.Substring(0, charsFit); + if (text.EndsWith(" ")) + text = text.Substring(0, text.Length - 1); + text += ellipsis; + } + break; + + case StringTrimming.EllipsisPath: + if (charsFit < text.Length) + { + while (text.Length > 3) + { + int mid = text.Length / 2; + string newText = text.Substring(0, mid) + ellipsis + text.Substring(mid + 1); + if (MeasureString(newText) == newText.Length) + { + text = newText; + break; + } + else + { + text = text.Remove(mid, 1); + } + } + } + break; + } + + lines.Add(new Line(text, this, originalCharIndex)); + } + } + + public StyleDescriptor WrapHtmlLines(StyleDescriptor style) + { + Line line = new Line("", this, this.originalCharIndex); + lines.Add(line); + Word word = new Word("", line); + line.Words.Add(word); + // for img + //RunImage img = null; + //end img + string text = this.text; + StringBuilder currentWord = new StringBuilder(100); + float width = 0; + bool skipSpace = true; + int originalCharIndex = this.originalCharIndex; + + for (int i = 0; i < text.Length; i++) + { + char lastChar = text[i]; + if (lastChar == '&') + { + if (Converter.FromHtmlEntities(text, ref i, currentWord)) + { + if (i >= text.Length - 1) + { + word.Runs.Add(new Run(currentWord.ToString(), style, word)); + // check width + width += word.Width + Renderer.SpaceWidth; + if (width > Renderer.DisplayRect.Width) + { + // line is too long, make a new line + if (line.Words.Count > 1) + { + // if line has several words, delete the last word from the current line + line.Words.RemoveAt(line.Words.Count - 1); + // make new line + line = new Line("", this, originalCharIndex); + // and add word to it + line.Words.Add(word); + word.SetLine(line); + lines.Add(line); + } + } +#if DOTNET_4 + currentWord.Clear(); // .NET 2.0 doesn't have Clear() +#else + currentWord.Length = 0; +#endif + lastChar = ' '; + } + else + { + if (currentWord[currentWord.Length - 1] == '\t') + { + currentWord.Length--; + lastChar = '\t'; + + } + else + { + continue; + } + } + } + } + if (lastChar == '<') + { + // probably html tag + StyleDescriptor newStyle = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine); + newStyle.Font = style.Font; + newStyle.Size = style.Size; + string tag = ""; + bool match = false; + + // , , + if (i + 3 <= text.Length) + { + match = true; + tag = text.Substring(i, 3).ToLower(); + if (tag == "") + newStyle.FontStyle |= FontStyle.Bold; + else if (tag == "") + newStyle.FontStyle |= FontStyle.Italic; + else if (tag == "") + newStyle.FontStyle |= FontStyle.Underline; + else + match = false; + + if (match) + i += 3; + } + + // , , + if (!match && i + 4 <= text.Length && text[i + 1] == '/') + { + match = true; + tag = text.Substring(i, 4).ToLower(); + if (tag == "") + newStyle.FontStyle &= ~FontStyle.Bold; + else if (tag == "") + newStyle.FontStyle &= ~FontStyle.Italic; + else if (tag == "") + newStyle.FontStyle &= ~FontStyle.Underline; + else + match = false; + + if (match) + i += 4; + } + + // , // ") + newStyle.BaseLine = BaseLine.Subscript; + else if (tag == "") + newStyle.BaseLine = BaseLine.Superscript; + else if (tag == "', i + 5); + if (right <= 0) match = false; + else + { + //found img and parse them + string src = null; + string alt = " "; + //currentWord = ""; + int src_ind = text.IndexOf("src=\"", i + 5); + if (src_ind < right && src_ind >= 0) + { + src_ind += 5; + int src_end = text.IndexOf("\"", src_ind); + if (src_end < right && src_end >= 0) + { + src = text.Substring(src_ind, src_end - src_ind); + } + } + int alt_ind = text.IndexOf("alt=\"", i + 5); + if (alt_ind < right && alt_ind >= 0) + { + alt_ind += 5; + int alt_end = text.IndexOf("\"", alt_ind); + if (alt_end < right && alt_end >= 0) + { + alt = text.Substring(alt_ind, alt_end - alt_ind); + } + } + //begin + if (currentWord.Length != 0) + { + // finish the word + word.Runs.Add(new Run(currentWord.ToString(), style, word)); + } +#if DOTNET_4 + currentWord.Clear(); // .NET 2.0 doesn't have Clear() +#else + currentWord.Length = 0; +#endif + //end + word.Runs.Add(new RunImage(src, alt, style, word)); + skipSpace = false; + i = right - 4; + } + } + else if (tag == "', i + 5); + if (right <= 0) match = false; + else + { + //found font and parse them + string color = null; + string face = null; + string size = null; + int color_ind = text.IndexOf("color=\"", i + 5); + if (color_ind < right && color_ind >= 0) + { + color_ind += 7; + int color_end = text.IndexOf("\"", color_ind); + if (color_end < right && color_end >= 0) + { + color = text.Substring(color_ind, color_end - color_ind); + } + } + + int face_ind = text.IndexOf("face=\"", i + 5); + if (face_ind < right && face_ind >= 0) + { + face_ind += 6; + int face_end = text.IndexOf("\"", face_ind); + if (face_end < right && face_end >= 0) + { + face = text.Substring(face_ind, face_end - face_ind); + } + } + + int size_ind = text.IndexOf("size=\"", i + 5); + if (size_ind < right && size_ind >= 0) + { + size_ind += 6; + int size_end = text.IndexOf("\"", size_ind); + if (size_end < right && size_end >= 0) + { + size = text.Substring(size_ind, size_end - size_ind); + } + } + + if (color != null) + { + if (color.StartsWith("\"") && color.EndsWith("\"")) + color = color.Substring(1, color.Length - 2); + if (color.StartsWith("#")) + { + newStyle.Color = Color.FromArgb((int)(0xFF000000 + uint.Parse(color.Substring(1), NumberStyles.HexNumber))); + } + else + { + newStyle.Color = Color.FromName(color); + } + } + newStyle.Font = face; + if (size != null) + { + + try + { + size = size.Trim(' '); + + switch (size[0]) + { + case '-': + size = size.Substring(1); + if (style.Size == 0) + newStyle.Size = Renderer.Font.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; + else + newStyle.Size = style.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; + break; + case '+': + size = size.Substring(1); + if (style.Size == 0) + newStyle.Size = Renderer.Font.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; + else + newStyle.Size = style.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; + break; + default: newStyle.Size = (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; break; + } + if (newStyle.Size < 0) newStyle.Size = 0; + } + catch { } + } + i = right - 4; + } + } + else + match = false; + + if (match) + i += 5; + } + + // , + if (!match && i + 6 <= text.Length && text[i + 1] == '/') + { + match = true; + tag = text.Substring(i, 6).ToLower(); + if (tag == "") + newStyle.BaseLine = BaseLine.Normal; + else if (tag == "") + newStyle.BaseLine = BaseLine.Normal; + else + match = false; + + if (match) + i += 6; + } + + // + if (!match && i + 8 <= text.Length && text.Substring(i, 8).ToLower() == "") + { + newStyle.FontStyle |= FontStyle.Strikeout; + match = true; + i += 8; + } + + // + if (!match && i + 9 <= text.Length && text.Substring(i, 9).ToLower() == "") + { + newStyle.FontStyle &= ~FontStyle.Strikeout; + match = true; + i += 9; + } + /* + // + if (!match && i + 7 <= text.Length && text.Substring(i, 7).ToLower() == "") + { + newStyle.Color = Renderer.BrushColor; + newStyle.Size = 0; + newStyle.Font = null; + match = true; + i += 7; + } + + if (match) + { + if (currentWord.Length != 0) + { + // finish the word + word.Runs.Add(new Run(currentWord.ToString(), style, word)); + } + +#if DOTNET_4 + currentWord.Clear(); // .NET 2.0 doesn't have Clear() +#else + currentWord.Length = 0; +#endif + style = newStyle; + i--; + + if (i >= text.Length - 1) + { + // check width + width += word.Width + Renderer.SpaceWidth; + if (width > Renderer.DisplayRect.Width) + { + // line is too long, make a new line + if (line.Words.Count > 1) + { + // if line has several words, delete the last word from the current line + line.Words.RemoveAt(line.Words.Count - 1); + // make new line + line = new Line("", this, originalCharIndex); + // and add word to it + line.Words.Add(word); + word.SetLine(line); + lines.Add(line); + } + } + } + continue; + } + } + if (lastChar == ' ' || lastChar == '\t' || i == text.Length - 1) + { + // finish the last word + bool isLastWord = i == text.Length - 1; + if (isLastWord) + { + currentWord.Append(lastChar); + skipSpace = false; + } + + if (lastChar == '\t') + skipSpace = false; + + // space + if (skipSpace) + { + currentWord.Append(lastChar); + } + else + { + // finish the word + if (currentWord.Length != 0) + word.Runs.Add(new Run(currentWord.ToString(), style, word)); + + // check width + width += word.Width + word.SpaceWidth; + if (width > Renderer.DisplayRect.Width) + { + // line is too long, make a new line + width = 0; + if (line.Words.Count > 1) + { + // if line has several words, delete the last word from the current line + line.Words.RemoveAt(line.Words.Count - 1); + // make new line + line = new Line("", this, originalCharIndex); + // and add word to it + line.Words.Add(word); + word.SetLine(line); + width += word.Width + word.SpaceWidth; + } + else + { + line = new Line("", this, i + 1); + } + lines.Add(line); + } + + // TAB symbol + if (lastChar == '\t') + { + if (currentWord.Length == 0 && line.Words.Count > 0 && line.Words[line.Words.Count - 1].Width == 0) + line.Words.RemoveAt(line.Words.Count - 1); + word = new Word("\t", line); + line.Words.Add(word); + // adjust width + width = Renderer.GetTabPosition(width); + } + + if (!isLastWord) + { + word = new Word("", line); + line.Words.Add(word); +#if DOTNET_4 + currentWord.Clear(); // .NET 2.0 doesn't have Clear() +#else + currentWord.Length = 0; +#endif + originalCharIndex = this.originalCharIndex + i + 1; + skipSpace = true; + } + } + } + else + { + // symbol + currentWord.Append(lastChar); + skipSpace = false; + } + } + + return style; + } + + public void AlignLines(bool forceJustify) + { + for (int i = 0; i < Lines.Count; i++) + { + HorzAlign align = Renderer.HorzAlign; + if (align == HorzAlign.Justify && i == Lines.Count - 1 && !forceJustify) + align = HorzAlign.Left; + Lines[i].AlignWords(align); + } + } + + public void Draw() + { + foreach (Line line in Lines) + { + line.Draw(); + } + } + #endregion + + public Paragraph(string text, AdvancedTextRenderer renderer, int originalCharIndex) + { + lines = new List(); + this.text = text; + this.renderer = renderer; + this.originalCharIndex = originalCharIndex; + } + } + + + /// + /// Line represents single text line. It consists of one or several . + /// Simple line (that does not contain tabs, html tags, and is not justified) has + /// single which contains all the text. + /// + public class Line + { + #region Fields + private List words; + private string text; + private bool hasTabs; + private Paragraph paragraph; + private float top; + private float width; + private int originalCharIndex; + private List underlines; + private List strikeouts; + #endregion + + #region Properties + public List Words + { + get { return words; } + } + + public string Text + { + get { return text; } + } + + public bool HasTabs + { + get { return hasTabs; } + } + + public float Left + { + get { return Words.Count > 0 ? Words[0].Left : 0; } + } + + public float Top + { + get { return top; } + set { top = value; } + } + + public float Width + { + get { return width; } + } + + public int OriginalCharIndex + { + get { return originalCharIndex; } + } + + public AdvancedTextRenderer Renderer + { + get { return paragraph.Renderer; } + } + + public StyleDescriptor Style + { + get + { + if (Words.Count > 0) + if (Words[0].Runs.Count > 0) + return Words[0].Runs[0].Style; + return null; + } + } + + public bool Last + { + get { return paragraph.Lines[paragraph.Lines.Count - 1] == this; } + } + + public List Underlines + { + get { return underlines; } + } + + public List Strikeouts + { + get { return strikeouts; } + } + #endregion + + #region Private Methods + private void PrepareUnderlines(List list, FontStyle style) + { + list.Clear(); + if (Words.Count == 0) + return; + + if (Renderer.HtmlTags) + { + float left = 0; + float right = 0; + bool styleOn = false; + + foreach (Word word in Words) + { + foreach (Run run in word.Runs) + { + using (Font fnt = run.GetFont()) + { + if ((fnt.Style & style) > 0) + { + if (!styleOn) + { + styleOn = true; + left = run.Left; + } + right = run.Left + run.Width; + } + if ((fnt.Style & style) == 0 && styleOn) + { + styleOn = false; + list.Add(new RectangleF(left, Top, right - left, 1)); + } + } + } + } + // close the style + if (styleOn) + list.Add(new RectangleF(left, Top, right - left, 1)); + } + else if ((Renderer.Font.Style & style) > 0) + { + float lineWidth = Width; + if (Renderer.HorzAlign == HorzAlign.Justify && (!Last || (paragraph.Last && Renderer.ForceJustify))) + lineWidth = Renderer.DisplayRect.Width - Renderer.SpaceWidth; + + list.Add(new RectangleF(Left, Top, lineWidth, 1)); + } + } + #endregion + + #region Public Methods + public void AlignWords(HorzAlign align) + { + width = 0; + + // handle each word + if (align == HorzAlign.Justify || HasTabs || Renderer.Wysiwyg || Renderer.HtmlTags) + { + float left = 0; + Word word = null; + for (int i = 0; i < Words.Count; i++) + { + word = Words[i]; + word.Left = left; + + if (word.Text == "\t") + { + left = Renderer.GetTabPosition(left); + // remove tab + Words.RemoveAt(i); + i--; + } + else + left += word.Width + word.SpaceWidth; + } + if (word != null) + width = left - word.SpaceWidth; + else + width = left - Renderer.SpaceWidth; + } + else + { + // join all words into one + Words.Clear(); + Words.Add(new Word(text, this)); + width = Words[0].Width; + } + + float rectWidth = Renderer.DisplayRect.Width; + if (align == HorzAlign.Justify) + { + float delta = (rectWidth - width - Renderer.SpaceWidth) / (Words.Count - 1); + float curDelta = delta; + for (int i = 1; i < Words.Count; i++) + { + words[i].Left += curDelta; + curDelta += delta; + } + } + else + { + float delta = 0; + if (align == HorzAlign.Center) + delta = (rectWidth - width) / 2; + else if (align == HorzAlign.Right) + delta = rectWidth - width - Renderer.SpaceWidth; + for (int i = 0; i < Words.Count; i++) + { + words[i].Left += delta; + } + } + + // adjust X offset + foreach (Word word in Words) + { + if (Renderer.RightToLeft) + word.Left = Renderer.DisplayRect.Right - word.Left; + else + word.Left += Renderer.DisplayRect.Left; + word.AdjustRuns(); + if (Renderer.RightToLeft && Renderer.PDFMode) + word.Left -= word.Width; + } + } + + public void MakeUnderlines() + { + PrepareUnderlines(underlines, FontStyle.Underline); + PrepareUnderlines(strikeouts, FontStyle.Strikeout); + } + + public void Draw() + { + foreach (Word word in Words) + { + word.Draw(); + } + + if (Underlines.Count > 0 || Strikeouts.Count > 0) + { + using (Pen pen = new Pen(Renderer.Brush, Renderer.Font.Size * 0.1f)) + { + float h = Renderer.FontLineHeight; + float w = h * 0.1f; // to match .net char X offset + // invert offset in case of rtl + if (Renderer.RightToLeft) + w = -w; + + // emulate underline & strikeout + foreach (RectangleF rect in Underlines) + { + Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h - w, rect.Right + w, rect.Top + h - w); + } + + h /= 2; + foreach (RectangleF rect in Strikeouts) + { + Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h, rect.Right + w, rect.Top + h); + } + } + } + } + + public float CalcHeight() + { + float height = -1; + foreach (Word word in Words) + { + height = Math.Max(height, word.CalcHeight()); + } + if (height < 0) + height = Renderer.LineHeight; + return height; + } + #endregion + + public Line(string text, Paragraph paragraph, int originalCharIndex) + { + this.words = new List(); + this.text = text; + this.paragraph = paragraph; + this.originalCharIndex = originalCharIndex; + underlines = new List(); + strikeouts = new List(); + hasTabs = text.Contains("\t"); + + // split text by spaces + string[] words = text.Split(new char[] { ' ' }); + string textWithSpaces = ""; + + foreach (string word in words) + { + if (word == "") + textWithSpaces += " "; + else + { + // split text by tabs + textWithSpaces += word; + string[] tabWords = textWithSpaces.Split(new char[] { '\t' }); + + foreach (string word1 in tabWords) + { + if (word1 == "") + this.words.Add(new Word("\t", this)); + else + { + this.words.Add(new Word(word1, this)); + this.words.Add(new Word("\t", this)); + } + } + + // remove last tab + this.words.RemoveAt(this.words.Count - 1); + + textWithSpaces = ""; + } + } + } - private void AdjustParagraphLines() - { - // calculate text height - float height = 0; - height = CalcHeight(); - /*foreach (Paragraph paragraph in Paragraphs) - { - height += paragraph.Lines.Count * LineHeight; - }*/ - - // calculate Y offset - float offsetY = DisplayRect.Top; - if (VertAlign == VertAlign.Center) - offsetY += (DisplayRect.Height - height) / 2; - else if (VertAlign == VertAlign.Bottom) - offsetY += (DisplayRect.Height - height) - 1; - - for (int i = 0; i < Paragraphs.Count; i++) - { - Paragraph paragraph = Paragraphs[i]; - paragraph.AlignLines(i == Paragraphs.Count - 1 && ForceJustify); - - // adjust line tops - foreach (Line line in paragraph.Lines) - { - line.Top = offsetY; - line.MakeUnderlines(); - offsetY += line.CalcHeight(); - } - } - } - #endregion + internal float CalcBaseLine() + { - #region Public Methods - public void Draw() - { - // set clipping - GraphicsState state = Graphics.Save(); - Graphics.SetClip(DisplayRect, CombineMode.Intersect); - - // reset alignment - StringAlignment saveAlign = Format.Alignment; - StringAlignment saveLineAlign = Format.LineAlignment; - Format.Alignment = StringAlignment.Near; - Format.LineAlignment = StringAlignment.Near; - - if (Angle != 0) - { - Graphics.TranslateTransform(DisplayRect.Left + DisplayRect.Width / 2, - DisplayRect.Top + DisplayRect.Height / 2); - Graphics.RotateTransform(Angle); - } - - Graphics.ScaleTransform(WidthRatio, 1); - - foreach (Paragraph paragraph in Paragraphs) - { - paragraph.Draw(); - } - - // restore alignment and clipping - Format.Alignment = saveAlign; - Format.LineAlignment = saveLineAlign; - Graphics.Restore(state); - } + float baseline = 0; + foreach (Word word in Words) + { + baseline = Math.Max(baseline, word.CalcBaseLine()); + } + return baseline; + } - public float CalcHeight() - { - int charsFit = 0; - StyleDescriptor style = null; - return CalcHeight(out charsFit, out style); - } + internal float CalcUnderBaseLine() + { - public float CalcHeight(out int charsFit, out StyleDescriptor style) - { - charsFit = 0; - style = null; - float height = 0; - float displayHeight = DisplayRect.Height; - if (LineHeight > displayHeight) - return 0; - - foreach (Paragraph paragraph in Paragraphs) - { - foreach (Line line in paragraph.Lines) - { - height += line.CalcHeight(); - if (charsFit == 0 && height > displayHeight) - { - charsFit = line.OriginalCharIndex; - if (HtmlTags) - style = line.Style; - } + float underbaseline = 0; + foreach (Word word in Words) + { + underbaseline = Math.Max(underbaseline, word.CalcUnderBaseLine()); + } + return underbaseline; + } } - } - - if (charsFit == 0) - charsFit = text.Length; - return height; - } - public float CalcWidth() - { - float width = 0; - foreach (Paragraph paragraph in Paragraphs) - { - foreach (Line line in paragraph.Lines) + /// + /// Word represents single word. It may consist of one or several , in case + /// when HtmlTags are enabled in the main class. + /// + public class Word { - if (width < line.Width) - width = line.Width; - } - } - return width + spaceWidth; - } - - internal float GetTabPosition(float pos) - { - float tabOffset = TabOffset; - float tabSize = TabSize; - int tabPosition = (int)((pos - tabOffset) / tabSize); - if (pos < tabOffset) - return tabOffset; - return (tabPosition + 1) * tabSize + tabOffset; - } - #endregion - - //public AdvancedTextRenderer(string text, Graphics g, Font font, Brush brush, Pen outlinePen, - // RectangleF rect, StringFormat format, HorzAlign horzAlign, VertAlign vertAlign, - // float lineHeight, int angle, float widthRatio, - // bool forceJustify, bool wysiwyg, bool htmlTags, bool pdfMode, - // float scale) - // : this (text, g, font, brush, outlinePen, - // rect, format, horzAlign, vertAlign, - // lineHeight, angle, widthRatio, - // forceJustify, wysiwyg, htmlTags, pdfMode, - // scale, null) - //{ - - //} - - public AdvancedTextRenderer(string text, Graphics g, Font font, Brush brush, Pen outlinePen, - RectangleF rect, StringFormat format, HorzAlign horzAlign, VertAlign vertAlign, - float lineHeight, int angle, float widthRatio, - bool forceJustify, bool wysiwyg, bool htmlTags, bool pdfMode, - float scale, float fontScale, InlineImageCache cache) - { - this.cache = cache; - this.scale = scale; - this.fontScale = fontScale; - paragraphs = new List(); - this.text = text; - graphics = g; - this.font = font; - this.brush = brush; - this.outlinePen = outlinePen; - displayRect = rect; - this.format = format; - this.horzAlign = horzAlign; - this.vertAlign = vertAlign; - this.lineHeight = lineHeight; - fontLineHeight = font.GetHeight(g); - if (this.lineHeight == 0) - this.lineHeight = fontLineHeight; - this.angle = angle % 360; - this.widthRatio = widthRatio; - this.forceJustify = forceJustify; - this.wysiwyg = wysiwyg; - this.htmlTags = htmlTags; - pDFMode = pdfMode; - spaceWidth = CalculateSpaceSize(g, font);// g.MeasureString(" ", font).Width; - - StringFormatFlags saveFlags = Format.FormatFlags; - StringTrimming saveTrimming = Format.Trimming; - - // match DrawString behavior: - // if height is less than 1.25 of font height, turn off word wrap - // commented out due to bug with band.break - //if (rect.Height < FFontLineHeight * 1.25f) - //FFormat.FormatFlags |= StringFormatFlags.NoWrap; - - // if word wrap is set, ignore trimming - if (WordWrap) - Format.Trimming = StringTrimming.Word; - - Format.FormatFlags = Format.FormatFlags | StringFormatFlags.MeasureTrailingSpaces; - - if (Angle != 0) - { - // shift displayrect - displayRect.X = -DisplayRect.Width / 2; - displayRect.Y = -DisplayRect.Height / 2; - - // rotate displayrect if angle is 90 or 270 - if ((Angle >= 90 && Angle < 180) || (Angle >= 270 && Angle < 360)) - displayRect = new RectangleF(DisplayRect.Y, DisplayRect.X, DisplayRect.Height, DisplayRect.Width); - } - - displayRect.X /= WidthRatio; - displayRect.Width /= WidthRatio; - - SplitToParagraphs(text); - AdjustParagraphLines(); - - // restore original values - displayRect = rect; - Format.FormatFlags = saveFlags; - Format.Trimming = saveTrimming; - } + #region Fields + private List runs; + protected string text; + private float left; + private float width; + internal Line line; + #endregion + + #region Properties + public string Text + { + get { return text; } + } + public float Left + { + get { return left; } + set { left = value; } + } - /// - /// Paragraph represents single paragraph. It consists of one or several . - /// - public class Paragraph - { - #region Fields - private List lines; - private AdvancedTextRenderer renderer; - private string text; - private int originalCharIndex; - #endregion - - #region Properties - public List Lines - { - get { return lines; } - } - - public AdvancedTextRenderer Renderer - { - get { return renderer; } - } - - public bool Last - { - get { return renderer.Paragraphs[renderer.Paragraphs.Count - 1] == this; } - } - - public bool IsEmpty - { - get { return String.IsNullOrEmpty(text); } - } - - public string Text - { - get { return text; } - } - #endregion - - #region Private Methods - private int MeasureString(string text) - { - if (text.Length > 0) + public float Width + { + get { - // BEGIN: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b - float left = 0; - int tabFit = 0; - while (text.Length > 0 && text[0] == '\t') + if (width == -1) { - left = Renderer.GetTabPosition(left); - text = text.Substring(1); - if (Renderer.DisplayRect.Width < left) - return tabFit; - tabFit++; + if (Renderer.HtmlTags) + { + width = 0; + foreach (Run run in Runs) + { + width += run.Width; + } + } + else + { + width = Renderer.Graphics.MeasureString(text, Renderer.Font, 10000, StringFormat.GenericTypographic).Width; + } } - if (tabFit > 0 && Renderer.DisplayRect.Width < left) - return tabFit; - int charsFit = 0; - int linesFit = 0; - // END: The fix for linux and core app a264aae5-193b-4e5c-955c-0818de3ca01b - Renderer.Graphics.MeasureString(text, Renderer.Font, - new SizeF(Renderer.DisplayRect.Width - left, Renderer.FontLineHeight + 1), - Renderer.Format, out charsFit, out linesFit); - return charsFit + tabFit; + return width; } - return 0; - } - #endregion - - #region Public Methods - public void WrapLines() - { - string text = this.text; - int charsFit = 0; + } - if (String.IsNullOrEmpty(text)) - { - lines.Add(new Line("", this, originalCharIndex)); - return; - } + public float Top + { + get { return line.Top; } + } - if (Renderer.WordWrap) - { - int originalCharIndex = this.originalCharIndex; - while (text.Length > 0) - { - charsFit = MeasureString(text); + public AdvancedTextRenderer Renderer + { + get { return line.Renderer; } + } - // avoid infinite loop when width of object less than width of one character - if (charsFit == 0) + public List Runs { - break; + get { return runs; } } - string textFit = text.Substring(0, charsFit).TrimEnd(new char[] { ' ' }); - lines.Add(new Line(textFit, this, originalCharIndex)); - text = text.Substring(charsFit) - // Fix for linux system - .TrimStart(' '); - originalCharIndex += charsFit; - } - } - else - { - string ellipsis = "\u2026"; - StringTrimming trimming = Renderer.Format.Trimming; - if (trimming == StringTrimming.EllipsisPath) - Renderer.Format.Trimming = StringTrimming.Character; - charsFit = MeasureString(text); - - switch (trimming) - { - case StringTrimming.Character: - case StringTrimming.Word: - text = text.Substring(0, charsFit); - break; - - case StringTrimming.EllipsisCharacter: - case StringTrimming.EllipsisWord: - if (charsFit < text.Length) - { - text = text.Substring(0, charsFit); - if (text.EndsWith(" ")) - text = text.Substring(0, text.Length - 1); - text += ellipsis; - } - break; - - case StringTrimming.EllipsisPath: - if (charsFit < text.Length) - { - while (text.Length > 3) + public float SpaceWidth + { + get { - int mid = text.Length / 2; - string newText = text.Substring(0, mid) + ellipsis + text.Substring(mid + 1); - if (MeasureString(newText) == newText.Length) - { - text = newText; - break; - } - else - { - text = text.Remove(mid, 1); - } + if (Runs == null || Runs.Count == 0) + return Renderer.SpaceWidth; + return Runs[Runs.Count - 1].SpaceWidth; } - } - break; - } + } + #endregion - lines.Add(new Line(text, this, originalCharIndex)); - } - } - - public StyleDescriptor WrapHtmlLines(StyleDescriptor style) - { - Line line = new Line("", this, this.originalCharIndex); - lines.Add(line); - Word word = new Word("", line); - line.Words.Add(word); - // for img - //RunImage img = null; - //end img - string text = this.text; - StringBuilder currentWord = new StringBuilder(100); - float width = 0; - bool skipSpace = true; - int originalCharIndex = this.originalCharIndex; - - for (int i = 0; i < text.Length; i++) - { - char lastChar = text[i]; - if (lastChar == '&') - { - if (Converter.FromHtmlEntities(text, ref i, currentWord)) - { - if (i >= text.Length - 1) - { - word.Runs.Add(new Run(currentWord.ToString(), style, word)); - // check width - width += word.Width + Renderer.SpaceWidth; - if (width > Renderer.DisplayRect.Width) - { - // line is too long, make a new line - if (line.Words.Count > 1) - { - // if line has several words, delete the last word from the current line - line.Words.RemoveAt(line.Words.Count - 1); - // make new line - line = new Line("", this, originalCharIndex); - // and add word to it - line.Words.Add(word); - word.SetLine(line); - lines.Add(line); - } - } -#if DOTNET_4 - currentWord.Clear(); // .NET 2.0 doesn't have Clear() -#else - currentWord.Length = 0; -#endif - lastChar = ' '; - } - else - { - if (currentWord[currentWord.Length - 1] == '\t') + #region Public Methods + public void AdjustRuns() + { + float left = Left; + foreach (Run run in Runs) { - currentWord.Length--; - lastChar = '\t'; + run.Left = left; - } - else - { - continue; - } - } - } - } - if (lastChar == '<') - { - // probably html tag - StyleDescriptor newStyle = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine); - newStyle.Font = style.Font; - newStyle.Size = style.Size; - string tag = ""; - bool match = false; - - // , , - if (i + 3 <= text.Length) - { - match = true; - tag = text.Substring(i, 3).ToLower(); - if (tag == "") - newStyle.FontStyle |= FontStyle.Bold; - else if (tag == "") - newStyle.FontStyle |= FontStyle.Italic; - else if (tag == "") - newStyle.FontStyle |= FontStyle.Underline; - else - match = false; - - if (match) - i += 3; - } - - // , , - if (!match && i + 4 <= text.Length && text[i + 1] == '/') - { - match = true; - tag = text.Substring(i, 4).ToLower(); - if (tag == "") - newStyle.FontStyle &= ~FontStyle.Bold; - else if (tag == "") - newStyle.FontStyle &= ~FontStyle.Italic; - else if (tag == "") - newStyle.FontStyle &= ~FontStyle.Underline; - else - match = false; - - if (match) - i += 4; - } - - // , // ") - newStyle.BaseLine = BaseLine.Subscript; - else if (tag == "") - newStyle.BaseLine = BaseLine.Superscript; - else if (tag == "', i + 5); - if (right <= 0) match = false; - else - { - //found img and parse them - string src = null; - string alt = " "; - //currentWord = ""; - int src_ind = text.IndexOf("src=\"", i + 5); - if (src_ind < right && src_ind >= 0) - { - src_ind += 5; - int src_end = text.IndexOf("\"", src_ind); - if (src_end < right && src_end >= 0) + if (Renderer.RightToLeft) { - src = text.Substring(src_ind, src_end - src_ind); + left -= run.Width; + if (Renderer.PDFMode) + run.Left -= run.Width; } - } - int alt_ind = text.IndexOf("alt=\"", i + 5); - if (alt_ind < right && alt_ind >= 0) - { - alt_ind += 5; - int alt_end = text.IndexOf("\"", alt_ind); - if (alt_end < right && alt_end >= 0) + else + left += run.Width; + } + } + + public void SetLine(Line line) + { + this.line = line; + } + + public void Draw() + { + if (Renderer.HtmlTags) + { + foreach (Run run in Runs) { - alt = text.Substring(alt_ind, alt_end - alt_ind); + run.Draw(); } - } - //begin - if (currentWord.Length != 0) - { - // finish the word - word.Runs.Add(new Run(currentWord.ToString(), style, word)); - } -#if DOTNET_4 - currentWord.Clear(); // .NET 2.0 doesn't have Clear() -#else - currentWord.Length = 0; -#endif - //end - word.Runs.Add(new RunImage(src, alt, style, word)); - skipSpace = false; - i = right - 4; } - } - else if (tag == "', i + 5); - if (right <= 0) match = false; else { - //found font and parse them - string color = null; - string face = null; - string size = null; - int color_ind = text.IndexOf("color=\"", i + 5); - if (color_ind < right && color_ind >= 0) - { - color_ind += 7; - int color_end = text.IndexOf("\"", color_ind); - if (color_end < right && color_end >= 0) + // don't draw underlines & strikeouts because they are drawn in the Line.Draw method + Font font = Renderer.Font; + bool disposeFont = false; + if ((Renderer.Font.Style & FontStyle.Underline) > 0 || (Renderer.Font.Style & FontStyle.Strikeout) > 0) { - color = text.Substring(color_ind, color_end - color_ind); + font = new Font(Renderer.Font, Renderer.Font.Style & ~FontStyle.Underline & ~FontStyle.Strikeout); + disposeFont = true; } - } - - int face_ind = text.IndexOf("face=\"", i + 5); - if (face_ind < right && face_ind >= 0) - { - face_ind += 6; - int face_end = text.IndexOf("\"", face_ind); - if (face_end < right && face_end >= 0) + + if (Renderer.OutlinePen == null) { - face = text.Substring(face_ind, face_end - face_ind); + Renderer.Graphics.DrawString(Text, font, Renderer.Brush, Left, Top, Renderer.Format); } - } - - int size_ind = text.IndexOf("size=\"", i + 5); - if (size_ind < right && size_ind >= 0) - { - size_ind += 6; - int size_end = text.IndexOf("\"", size_ind); - if (size_end < right && size_end >= 0) + else { - size = text.Substring(size_ind, size_end - size_ind); + GraphicsPath path = new GraphicsPath(); + path.AddString(Text, font.FontFamily, Convert.ToInt32(font.Style), Renderer.Graphics.DpiY * font.Size / 72, new PointF(Left - 1, Top - 1), Renderer.Format); + Renderer.Graphics.FillPath(Renderer.Brush, path); + Renderer.Graphics.DrawPath(Renderer.OutlinePen, path); } - } - if (color != null) - { - if (color.StartsWith("\"") && color.EndsWith("\"")) - color = color.Substring(1, color.Length - 2); - if (color.StartsWith("#")) + if (disposeFont) { - newStyle.Color = Color.FromArgb((int)(0xFF000000 + uint.Parse(color.Substring(1), NumberStyles.HexNumber))); + font.Dispose(); + font = null; } - else + } + } + + internal float CalcHeight() + { + if (Renderer.HtmlTags) + { + float height = -1; + foreach (Run run in Runs) { - newStyle.Color = Color.FromName(color); + height = Math.Max(height, run.Height); } - } - newStyle.Font = face; - if (size != null) - { + if (height < 0) + height = Renderer.LineHeight; + return height; + } + else + { + return Renderer.LineHeight; + } + } - try + internal float CalcBaseLine() + { + float baseLine = 0; + if (Renderer.HtmlTags) + { + foreach (Run run in Runs) { - size = size.Trim(' '); - - switch(size[0]) - { - case '-': - size = size.Substring(1); - if (style.Size == 0) - newStyle.Size = Renderer.Font.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; - else - newStyle.Size = style.Size - (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; - break; - case '+': - size = size.Substring(1); - if (style.Size == 0) - newStyle.Size = Renderer.Font.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; - else - newStyle.Size = style.Size + (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; - break; - default: newStyle.Size = (float)Converter.FromString(typeof(float), size) * Renderer.FontScale; break; - } - if (newStyle.Size < 0) newStyle.Size = 0; + baseLine = Math.Max(baseLine, run.CurrentBaseLine); } - catch { } - } - i = right - 4; + return baseLine; + } + else + { + return 0; } - } - else - match = false; + } - if (match) - i += 5; + internal float CalcUnderBaseLine() + { + float underbaseLine = 0; + if (Renderer.HtmlTags) + { + foreach (Run run in Runs) + { + underbaseLine = Math.Max(underbaseLine, run.CurrentUnderBaseLine); + } + return underbaseLine; + } + else + { + return 0; + } } + #endregion - // , - if (!match && i + 6 <= text.Length && text[i + 1] == '/') + public Word(string text, Line line) { - match = true; - tag = text.Substring(i, 6).ToLower(); - if (tag == "") - newStyle.BaseLine = BaseLine.Normal; - else if (tag == "") - newStyle.BaseLine = BaseLine.Normal; - else - match = false; + this.text = text; + runs = new List(); + this.line = line; + width = -1; + } + } + - if (match) - i += 6; + + + /// + /// Represents character placement. + /// + public enum BaseLine + { + Normal, + Subscript, + Superscript + } + + + /// + /// Represents a style used in HtmlTags mode. + /// + public class StyleDescriptor + { + #region Fields + private FontStyle fontStyle; + private Color color; + private BaseLine baseLine; + private string font; + private float size; + #endregion + + #region Properties + public FontStyle FontStyle + { + get { return fontStyle; } + set { fontStyle = value; } } - // - if (!match && i + 8 <= text.Length && text.Substring(i, 8).ToLower() == "") + public string Font { - newStyle.FontStyle |= FontStyle.Strikeout; - match = true; - i += 8; + get { return font; } + set { font = value; } } - // - if (!match && i + 9 <= text.Length && text.Substring(i, 9).ToLower() == "") + public float Size { - newStyle.FontStyle &= ~FontStyle.Strikeout; - match = true; - i += 9; + get { return size; } + set { size = value; } } - /* - // - if (!match && i + 7 <= text.Length && text.Substring(i, 7).ToLower() == "") + #endregion + + #region Public Methods + public override string ToString() { - newStyle.Color = Renderer.BrushColor; - newStyle.Size = 0; - newStyle.Font = null; - match = true; - i += 7; + string result = ""; + + if ((FontStyle & FontStyle.Bold) != 0) + result += ""; + if ((FontStyle & FontStyle.Italic) != 0) + result += ""; + if ((FontStyle & FontStyle.Underline) != 0) + result += ""; + if ((FontStyle & FontStyle.Strikeout) != 0) + result += ""; + if (BaseLine == BaseLine.Subscript) + result += ""; + if (BaseLine == BaseLine.Superscript) + result += ""; + + result += "= text.Length - 1) - { - // check width - width += word.Width + Renderer.SpaceWidth; - if (width > Renderer.DisplayRect.Width) - { - // line is too long, make a new line - if (line.Words.Count > 1) - { - // if line has several words, delete the last word from the current line - line.Words.RemoveAt(line.Words.Count - 1); - // make new line - line = new Line("", this, originalCharIndex); - // and add word to it - line.Words.Add(word); - word.SetLine(line); - lines.Add(line); - } - } - } - continue; + + /// + /// Represents sequence of characters that have the same . + /// + public class Run + { + #region Fields + protected string text; + private StyleDescriptor style; + protected Word word; + private float left; + protected float width; + protected float lineHeight; + protected float fontLineHeight; + private float baseLine; + protected float underBaseLine; + protected float spaceWidth; + #endregion + + #region Properties + public string Text + { + get { return text; } } - } - if (lastChar == ' ' || lastChar == '\t' || i == text.Length - 1) - { - // finish the last word - bool isLastWord = i == text.Length - 1; - if (isLastWord) + + public StyleDescriptor Style { - currentWord.Append(lastChar); - skipSpace = false; + get { return style; } } - if (lastChar == '\t') - skipSpace = false; + public AdvancedTextRenderer Renderer + { + get { return word.Renderer; } + } - // space - if (skipSpace) + public float Left { - currentWord.Append(lastChar); + get { return left; } + set { left = value; } } - else + + public float LineHeight { - // finish the word - if (currentWord.Length != 0) - word.Runs.Add(new Run(currentWord.ToString(), style, word)); + get + { + if (lineHeight == 0) + { + if (style.Font == null && style.Size <= 0) + lineHeight = Renderer.LineHeight; + else + lineHeight = GetFont().GetHeight(Renderer.Graphics); + } + return lineHeight; + } + } - // check width - width += word.Width + word.SpaceWidth; - if (width > Renderer.DisplayRect.Width) - { - // line is too long, make a new line - width = 0; - if (line.Words.Count > 1) + virtual public float CurrentBaseLine + { + get { - // if line has several words, delete the last word from the current line - line.Words.RemoveAt(line.Words.Count - 1); - // make new line - line = new Line("", this, originalCharIndex); - // and add word to it - line.Words.Add(word); - word.SetLine(line); - width += word.Width + word.SpaceWidth; + if (baseLine < 0) + { + Font ff = GetFont(); + float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle); + float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle); + baseLine = FontLineHeight * ascent / lineSpace; + underBaseLine = FontLineHeight - baseLine; + } + return baseLine; } - else + } + + virtual public float CurrentUnderBaseLine + { + get { - line = new Line("", this, i + 1); + if (underBaseLine < 0) + { + Font ff = GetFont(); + float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle); + float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle); + baseLine = FontLineHeight * ascent / lineSpace; + underBaseLine = FontLineHeight - baseLine; + } + return baseLine; } - lines.Add(line); - } - - // TAB symbol - if (lastChar == '\t') - { - if (currentWord.Length == 0 && line.Words.Count > 0 && line.Words[line.Words.Count-1].Width == 0) - line.Words.RemoveAt(line.Words.Count - 1); - word = new Word("\t", line); - line.Words.Add(word); - // adjust width - width = Renderer.GetTabPosition(width); - } + } - if (!isLastWord) - { - word = new Word("", line); - line.Words.Add(word); -#if DOTNET_4 - currentWord.Clear(); // .NET 2.0 doesn't have Clear() -#else - currentWord.Length = 0; -#endif - originalCharIndex = this.originalCharIndex + i + 1; - skipSpace = true; - } - } - } - else - { - // symbol - currentWord.Append(lastChar); - skipSpace = false; - } - } + public float FontLineHeight + { + get + { + if (fontLineHeight == 0) + { + if (style.Font == null && style.Size <= 0) + fontLineHeight = Renderer.FontLineHeight; + else + fontLineHeight = GetFont().GetHeight(Renderer.Graphics); + } + return fontLineHeight; + } + } - return style; - } + public virtual float Top + { + get + { + float baseLine = 0; + if (Style.BaseLine == BaseLine.Subscript) + baseLine += FontLineHeight * 0.45f; + else if (Style.BaseLine == BaseLine.Superscript) + baseLine -= FontLineHeight * 0.15f; + return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine; + } + } - public void AlignLines(bool forceJustify) - { - for (int i = 0; i < Lines.Count; i++) - { - HorzAlign align = Renderer.HorzAlign; - if (align == HorzAlign.Justify && i == Lines.Count - 1 && !forceJustify) - align = HorzAlign.Left; - Lines[i].AlignWords(align); - } - } + virtual public float Width + { + get { return width; } + } - public void Draw() - { - foreach (Line line in Lines) - { - line.Draw(); - } - } -#endregion - - public Paragraph(string text, AdvancedTextRenderer renderer, int originalCharIndex) - { - lines = new List(); - this.text = text; - this.renderer = renderer; - this.originalCharIndex = originalCharIndex; - } - } + virtual public float Height + { + get + { + return LineHeight; + } + } - /// - /// Line represents single text line. It consists of one or several . - /// Simple line (that does not contain tabs, html tags, and is not justified) has - /// single which contains all the text. - /// - public class Line - { -#region Fields - private List words; - private string text; - private bool hasTabs; - private Paragraph paragraph; - private float top; - private float width; - private int originalCharIndex; - private List underlines; - private List strikeouts; -#endregion - -#region Properties - public List Words - { - get { return words; } - } - - public string Text - { - get { return text; } - } - - public bool HasTabs - { - get { return hasTabs; } - } - - public float Left - { - get { return Words.Count > 0 ? Words[0].Left : 0; } - } - - public float Top - { - get { return top; } - set { top = value; } - } - - public float Width - { - get { return width; } - } - - public int OriginalCharIndex - { - get { return originalCharIndex; } - } - - public AdvancedTextRenderer Renderer - { - get { return paragraph.Renderer; } - } - - public StyleDescriptor Style - { - get - { - if (Words.Count > 0) - if (Words[0].Runs.Count > 0) - return Words[0].Runs[0].Style; - return null; - } - } - - public bool Last - { - get { return paragraph.Lines[paragraph.Lines.Count - 1] == this; } - } - - public List Underlines - { - get { return underlines; } - } - - public List Strikeouts - { - get { return strikeouts; } - } -#endregion - -#region Private Methods - private void PrepareUnderlines(List list, FontStyle style) - { - list.Clear(); - if (Words.Count == 0) - return; - - if (Renderer.HtmlTags) - { - float left = 0; - float right = 0; - bool styleOn = false; - foreach (Word word in Words) - { - foreach (Run run in word.Runs) + public float SpaceWidth { - using (Font fnt = run.GetFont()) - { - if ((fnt.Style & style) > 0) - { - if (!styleOn) - { - styleOn = true; - left = run.Left; - } - right = run.Left + run.Width; - } - if ((fnt.Style & style) == 0 && styleOn) + get { - styleOn = false; - list.Add(new RectangleF(left, Top, right - left, 1)); + if (spaceWidth < 0) + { + spaceWidth = CalculateSpaceSize(Renderer.Graphics, GetFont());// Renderer.Graphics.MeasureString(" ", GetFont()).Width; + } + return spaceWidth; } - } } - } - // close the style - if (styleOn) - list.Add(new RectangleF(left, Top, right - left, 1)); - } - else if ((Renderer.Font.Style & style) > 0) - { - float lineWidth = Width; - if (Renderer.HorzAlign == HorzAlign.Justify && (!Last || (paragraph.Last && Renderer.ForceJustify))) - lineWidth = Renderer.DisplayRect.Width - Renderer.SpaceWidth; + #endregion - list.Add(new RectangleF(Left, Top, lineWidth, 1)); - } - } -#endregion + #region Private Methods + private Font GetFont(bool disableUnderlinesStrikeouts) + { + float fontSize = Renderer.Font.Size; + if (Style.Size != 0) + fontSize = Style.Size; + if (Style.BaseLine != BaseLine.Normal) + fontSize *= 0.6f; + + FontStyle fontStyle = Style.FontStyle; + if (disableUnderlinesStrikeouts) + fontStyle = fontStyle & ~FontStyle.Underline & ~FontStyle.Strikeout; + if (Style.Font != null) + return new Font(Style.Font, fontSize, fontStyle); + return new Font(Renderer.Font.Name, fontSize, fontStyle); + } + #endregion -#region Public Methods - public void AlignWords(HorzAlign align) - { - width = 0; + #region Public Methods + public Font GetFont() + { + return GetFont(false); + } - // handle each word - if (align == HorzAlign.Justify || HasTabs || Renderer.Wysiwyg || Renderer.HtmlTags) - { - float left = 0; - Word word = null; - for (int i = 0; i < Words.Count; i++) - { - word = Words[i]; - word.Left = left; + public Brush GetBrush() + { + return new SolidBrush(Style.Color); + } - if (word.Text == "\t") + public virtual void Draw() { - left = Renderer.GetTabPosition(left); - // remove tab - Words.RemoveAt(i); - i--; + using (Font font = GetFont(true)) + using (Brush brush = GetBrush()) + { + Renderer.Graphics.DrawString(text, font, brush, Left, Top, Renderer.Format); + } } - else - left += word.Width + word.SpaceWidth; - } - if (word != null) - width = left - word.SpaceWidth; - else - width = left - Renderer.SpaceWidth; - } - else - { - // join all words into one - Words.Clear(); - Words.Add(new Word(text, this)); - width = Words[0].Width; - } + #endregion - float rectWidth = Renderer.DisplayRect.Width; - if (align == HorzAlign.Justify) - { - float delta = (rectWidth - width - Renderer.SpaceWidth) / (Words.Count - 1); - float curDelta = delta; - for (int i = 1; i < Words.Count; i++) - { - words[i].Left += curDelta; - curDelta += delta; - } - } - else - { - float delta = 0; - if (align == HorzAlign.Center) - delta = (rectWidth - width) / 2; - else if (align == HorzAlign.Right) - delta = rectWidth - width - Renderer.SpaceWidth; - for (int i = 0; i < Words.Count; i++) - { - words[i].Left += delta; - } + public Run(string text, StyleDescriptor style, Word word) + { + baseLine = float.MinValue; + underBaseLine = float.MinValue; + this.text = text; + this.style = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine); + this.style.Font = style.Font; + this.style.Size = style.Size; + this.word = word; + spaceWidth = -1; + + using (Font font = GetFont()) + { + width = Renderer.Graphics.MeasureString(text, font, 10000, StringFormat.GenericTypographic).Width; + } + } } - // adjust X offset - foreach (Word word in Words) + /// + /// Represents inline Image. + /// + internal class RunImage : Run { - if (Renderer.RightToLeft) - word.Left = Renderer.DisplayRect.Right - word.Left; - else - word.Left += Renderer.DisplayRect.Left; - word.AdjustRuns(); - if (Renderer.RightToLeft && Renderer.PDFMode) - word.Left -= word.Width; - } - } + public Image Image { get { return image; } } + override public float Width + { + get + { + if (Image == null) return base.Width; + return Image.Width; + } + } + + override public float Top + { + get + { + float baseLine = 0; + if (Style.BaseLine == BaseLine.Subscript) + baseLine += FontLineHeight * 0.45f; + else if (Style.BaseLine == BaseLine.Superscript) + baseLine -= FontLineHeight * 0.15f; + return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine; + } + } - public void MakeUnderlines() - { - PrepareUnderlines(underlines, FontStyle.Underline); - PrepareUnderlines(strikeouts, FontStyle.Strikeout); - } + override public float CurrentBaseLine + { + get + { + if (Image == null) return base.CurrentBaseLine; + return Image.Height; + } + } - public void Draw() - { - foreach (Word word in Words) - { - word.Draw(); - } + override public float Height + { + get + { + if (Image == null) return base.Height; + return Image.Height + word.line.CalcUnderBaseLine(); + } + } - if (Underlines.Count > 0 || Strikeouts.Count > 0) - { - using (Pen pen = new Pen(Renderer.Brush, Renderer.Font.Size * 0.1f)) - { - float h = Renderer.FontLineHeight; - float w = h * 0.1f; // to match .net char X offset - // invert offset in case of rtl - if (Renderer.RightToLeft) - w = -w; + private Image image; - // emulate underline & strikeout - foreach (RectangleF rect in Underlines) + override public void Draw() { - Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h - w, rect.Right + w, rect.Top + h - w); + if (Image == null) + { + base.Draw(); + return; + } + Renderer.Graphics.DrawImage(Image, Left, Top);// (FText, font, brush, Left, Top, Renderer.Format); } - h /= 2; - foreach (RectangleF rect in Strikeouts) + public static Bitmap ResizeImage(Image image, float scale) { - Renderer.Graphics.DrawLine(pen, rect.Left + w, rect.Top + h, rect.Right + w, rect.Top + h); - } - } - } - } + int width = (int)(image.Width * scale); + int height = (int)(image.Height * scale); + if (width == 0) width = 1; + if (height == 0) height = 1; + Rectangle destRect = new Rectangle(0, 0, width, height); + Bitmap destImage = new Bitmap(width, height); - public float CalcHeight() - { - float height = -1; - foreach (Word word in Words) - { - height = Math.Max(height, word.CalcHeight()); - } - if (height < 0) - height = Renderer.LineHeight; - return height; - } -#endregion - - public Line(string text, Paragraph paragraph, int originalCharIndex) - { - this.words = new List(); - this.text = text; - this.paragraph = paragraph; - this.originalCharIndex = originalCharIndex; - underlines = new List(); - strikeouts = new List(); - hasTabs = text.Contains("\t"); - - // split text by spaces - string[] words = text.Split(new char[] { ' ' }); - string textWithSpaces = ""; - - foreach (string word in words) - { - if (word == "") - textWithSpaces += " "; - else - { - // split text by tabs - textWithSpaces += word; - string[] tabWords = textWithSpaces.Split(new char[] { '\t' }); - - foreach (string word1 in tabWords) - { - if (word1 == "") - this.words.Add(new Word("\t", this)); - else - { - this.words.Add(new Word(word1, this)); - this.words.Add(new Word("\t", this)); - } - } - - // remove last tab - this.words.RemoveAt(this.words.Count - 1); - - textWithSpaces = ""; - } - } - } + destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); - internal float CalcBaseLine() - { + using (Graphics graphics = Graphics.FromImage(destImage)) + { + graphics.CompositingMode = CompositingMode.SourceCopy; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - float baseline = 0; - foreach (Word word in Words) - { - baseline = Math.Max(baseline, word.CalcBaseLine()); - } - return baseline; - } + using (System.Drawing.Imaging.ImageAttributes wrapMode = new System.Drawing.Imaging.ImageAttributes()) + { + wrapMode.SetWrapMode(WrapMode.TileFlipXY); + graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); + } + } - internal float CalcUnderBaseLine() - { + return destImage; + } - float underbaseline = 0; - foreach (Word word in Words) - { - underbaseline = Math.Max(underbaseline, word.CalcUnderBaseLine()); + public RunImage(string src, string text, StyleDescriptor style, Word word) : base(text, style, word) + { + underBaseLine = 0; + image = ResizeImage(InlineImageCache.Load(Renderer.Cache, src), Renderer.Scale); + } } - return underbaseline; - } } /// - /// Word represents single word. It may consist of one or several , in case - /// when HtmlTags are enabled in the main class. + /// Standard text renderer uses standard DrawString method to draw text. It also supports: + /// - text rotation; + /// - fonts with non-standard width ratio. + /// In case your text is justified, or contains html tags, use the + /// class instead. /// - public class Word + internal class StandardTextRenderer { -#region Fields - private List runs; - protected string text; - private float left; - private float width; - internal Line line; -#endregion - -#region Properties - public string Text - { - get { return text; } - } - - public float Left - { - get { return left; } - set { left = value; } - } - - public float Width - { - get + public static void Draw(string text, Graphics g, Font font, Brush brush, Pen outlinePen, + RectangleF rect, StringFormat format, int angle, float widthRatio) { - if (width == -1) - { - if (Renderer.HtmlTags) + GraphicsState state = g.Save(); + g.SetClip(rect, CombineMode.Intersect); + g.TranslateTransform(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); + g.RotateTransform(angle); + rect.X = -rect.Width / 2; + rect.Y = -rect.Height / 2; + + if ((angle >= 90 && angle < 180) || (angle >= 270 && angle < 360)) + rect = new RectangleF(rect.Y, rect.X, rect.Height, rect.Width); + + g.ScaleTransform(widthRatio, 1); + rect.X /= widthRatio; + rect.Width /= widthRatio; + + if (outlinePen == null) { - width = 0; - foreach (Run run in Runs) - { - width += run.Width; - } + g.DrawString(text, font, brush, rect, format); } else { - width = Renderer.Graphics.MeasureString(text, Renderer.Font, 10000, StringFormat.GenericTypographic).Width; + GraphicsPath path = new GraphicsPath(); + path.AddString(text, font.FontFamily, Convert.ToInt32(font.Style), g.DpiY * font.Size / 72, rect, format); + g.FillPath(brush, path); + g.DrawPath(outlinePen, path); } - } - return width; - } - } - - public float Top - { - get { return line.Top; } - } - - public AdvancedTextRenderer Renderer - { - get { return line.Renderer; } - } - - public List Runs - { - get { return runs; } - } - - public float SpaceWidth - { - get - { - if (Runs == null || Runs.Count == 0) - return Renderer.SpaceWidth; - return Runs[Runs.Count - 1].SpaceWidth; - } - } -#endregion - -#region Public Methods - public void AdjustRuns() - { - float left = Left; - foreach (Run run in Runs) - { - run.Left = left; - - if (Renderer.RightToLeft) - { - left -= run.Width; - if (Renderer.PDFMode) - run.Left -= run.Width; - } - else - left += run.Width; - } - } - public void SetLine(Line line) - { - this.line = line; - } - - public void Draw() - { - if (Renderer.HtmlTags) - { - foreach (Run run in Runs) - { - run.Draw(); - } - } - else - { - // don't draw underlines & strikeouts because they are drawn in the Line.Draw method - Font font = Renderer.Font; - bool disposeFont = false; - if ((Renderer.Font.Style & FontStyle.Underline) > 0 || (Renderer.Font.Style & FontStyle.Strikeout) > 0) - { - font = new Font(Renderer.Font, Renderer.Font.Style & ~FontStyle.Underline & ~FontStyle.Strikeout); - disposeFont = true; - } - - if (Renderer.OutlinePen == null) - { - Renderer.Graphics.DrawString(Text, font, Renderer.Brush, Left, Top, Renderer.Format); - } - else - { - GraphicsPath path = new GraphicsPath(); - path.AddString(Text, font.FontFamily, Convert.ToInt32(font.Style), Renderer.Graphics.DpiY * font.Size / 72, new PointF(Left - 1, Top - 1), Renderer.Format); - Renderer.Graphics.FillPath(Renderer.Brush, path); - Renderer.Graphics.DrawPath(Renderer.OutlinePen, path); - } - - if (disposeFont) - { - font.Dispose(); - font = null; - } + g.Restore(state); } - } + } - internal float CalcHeight() - { - if (Renderer.HtmlTags) - { - float height = -1; - foreach (Run run in Runs) - { - height = Math.Max(height, run.Height); - } - if (height < 0) - height = Renderer.LineHeight; - return height; - } - else - { - return Renderer.LineHeight; - } - } + /// + /// Cache for rendering img tags in textobject. + /// You can use only HTTP[s] protocol with absolute urls. + /// + public class InlineImageCache : IDisposable + { + #region Private Fields - internal float CalcBaseLine() - { - float baseLine = 0; - if (Renderer.HtmlTags) - { - foreach (Run run in Runs) - { - baseLine = Math.Max(baseLine, run.CurrentBaseLine); - } - return baseLine; - } - else - { - return 0; - } - } + private WebClient client; - internal float CalcUnderBaseLine() - { - float underbaseLine = 0; - if (Renderer.HtmlTags) - { - foreach (Run run in Runs) - { - underbaseLine = Math.Max(underbaseLine, run.CurrentUnderBaseLine); - } - return underbaseLine; - } - else - { - return 0; - } - } -#endregion - - public Word(string text, Line line) - { - this.text = text; - runs = new List(); - this.line = line; - width = -1; - } - } + private Dictionary items; + private bool serialized; + private object locker; + #endregion Private Fields - /// - /// Represents character placement. - /// - public enum BaseLine - { - Normal, - Subscript, - Superscript - } + #region Public Properties + /// + /// Is serialized + /// + public bool Serialized { get { return serialized; } set { serialized = value; } } - /// - /// Represents a style used in HtmlTags mode. - /// - public class StyleDescriptor - { -#region Fields - private FontStyle fontStyle; - private Color color; - private BaseLine baseLine; - private string font; - private float size; -#endregion - -#region Properties - public FontStyle FontStyle - { - get { return fontStyle; } - set { fontStyle = value; } - } - - public string Font - { - get { return font; } - set { font = value; } - } - - public float Size - { - get { return size; } - set { size = value; } - } - public Color Color - { - get { return color; } - set { color = value; } - } - - public BaseLine BaseLine - { - get { return baseLine; } - set { baseLine = value; } - } -#endregion - -#region Public Methods - public override string ToString() - { - string result = ""; - - if ((FontStyle & FontStyle.Bold) != 0) - result += ""; - if ((FontStyle & FontStyle.Italic) != 0) - result += ""; - if ((FontStyle & FontStyle.Underline) != 0) - result += ""; - if ((FontStyle & FontStyle.Strikeout) != 0) - result += ""; - if (BaseLine == BaseLine.Subscript) - result += ""; - if (BaseLine == BaseLine.Superscript) - result += ""; - - result += " - /// Represents sequence of characters that have the same . - ///
- public class Run - { -#region Fields - protected string text; - private StyleDescriptor style; - protected Word word; - private float left; - protected float width; - protected float lineHeight; - protected float fontLineHeight; - private float baseLine; - protected float underBaseLine; - protected float spaceWidth; -#endregion - -#region Properties - public string Text - { - get { return text; } - } - - public StyleDescriptor Style - { - get { return style; } - } - - public AdvancedTextRenderer Renderer - { - get { return word.Renderer; } - } - - public float Left - { - get { return left; } - set { left = value; } - } - - public float LineHeight - { - get + /// + /// Get or set WebClient for downloading imgs by url + /// + private WebClient Client { - if(lineHeight == 0) - { - if (style.Font == null && style.Size <=0) - lineHeight = Renderer.LineHeight; - else - lineHeight = GetFont().GetHeight(Renderer.Graphics); - } - return lineHeight; + get + { + if (client == null) + { + client = new WebClient(); + } + return client; + } + set + { + client = value; + } } - } - virtual public float CurrentBaseLine - { - get - { - if(baseLine < 0) - { - Font ff = GetFont(); - float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle); - float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle); - baseLine = FontLineHeight * ascent / lineSpace; - underBaseLine = FontLineHeight - baseLine; - } - return baseLine; - } - } + #endregion Private Properties - virtual public float CurrentUnderBaseLine - { - get - { - if (underBaseLine < 0) - { - Font ff = GetFont(); - float lineSpace = ff.FontFamily.GetLineSpacing(Style.FontStyle); - float ascent = ff.FontFamily.GetCellAscent(Style.FontStyle); - baseLine = FontLineHeight * ascent / lineSpace; - underBaseLine = FontLineHeight - baseLine; - } - return baseLine; - } - } + #region Public Events - public float FontLineHeight - { - get - { - if(fontLineHeight == 0) - { - if (style.Font == null && style.Size <=0) - fontLineHeight = Renderer.FontLineHeight; - else - fontLineHeight = GetFont().GetHeight(Renderer.Graphics); - } - return fontLineHeight; - } - } + /// + /// Occurs before image load + /// + public static event EventHandler AfterLoad; + /// + /// Occurs after image load + /// + public static event EventHandler BeforeLoad; - public virtual float Top - { - get - { - float baseLine = 0; - if (Style.BaseLine == BaseLine.Subscript) - baseLine += FontLineHeight * 0.45f; - else if (Style.BaseLine == BaseLine.Superscript) - baseLine -= FontLineHeight * 0.15f; - return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine; - } - } + #endregion Public Events - virtual public float Width - { - get { return width; } - } + #region Public Methods - virtual public float Height - { - get + /// + /// Enumerates all values + /// + /// + public IEnumerable AllItems() { - return LineHeight; + List list = new List(); + lock (locker) + { + if (items != null) + { + foreach (KeyValuePair item in items) + { + item.Value.Src = item.Key; + list.Add(item.Value); + } + } + } + return list; } - } - - - public float SpaceWidth - { - get - { - if(spaceWidth < 0) - { - spaceWidth = CalculateSpaceSize(Renderer.Graphics, GetFont());// Renderer.Graphics.MeasureString(" ", GetFont()).Width; - } - return spaceWidth; - } - } -#endregion - -#region Private Methods - private Font GetFont(bool disableUnderlinesStrikeouts) - { - float fontSize = Renderer.Font.Size; - if (Style.Size != 0) - fontSize = Style.Size; - if (Style.BaseLine != BaseLine.Normal) - fontSize *= 0.6f; - - FontStyle fontStyle = Style.FontStyle; - if (disableUnderlinesStrikeouts) - fontStyle = fontStyle & ~FontStyle.Underline & ~FontStyle.Strikeout; - if(Style.Font!=null) - return new Font(Style.Font, fontSize, fontStyle); - return new Font(Renderer.Font.Name, fontSize, fontStyle); - } -#endregion - -#region Public Methods - public Font GetFont() - { - return GetFont(false); - } - - public Brush GetBrush() - { - return new SolidBrush(Style.Color); - } - - public virtual void Draw() - { - using (Font font = GetFont(true)) - using (Brush brush = GetBrush()) + /// + /// Return CacheItem by src + /// + /// Src attribute from img tag + /// + public CacheItem Get(string src) { - Renderer.Graphics.DrawString(text, font, brush, Left, Top, Renderer.Format); + CacheItem item = null; + if (!Validate(src)) + item = new CacheItem(); + if (String.IsNullOrEmpty(src)) + return item; + lock (locker) + { + if (items == null) + { + items = new Dictionary(); + if (item == null) + item = new CacheItem(); + items[src] = item; + Serialized = false; + } + if (items.ContainsKey(src)) + return items[src]; + } + return item; } - } -#endregion - - public Run(string text, StyleDescriptor style, Word word) - { - baseLine = float.MinValue; - underBaseLine = float.MinValue; - this.text = text; - this.style = new StyleDescriptor(style.FontStyle, style.Color, style.BaseLine); - this.style.Font = style.Font; - this.style.Size = style.Size; - this.word = word; - spaceWidth = -1; - - using (Font font = GetFont()) - { - width = Renderer.Graphics.MeasureString(text, font, 10000, StringFormat.GenericTypographic).Width; - } - } - } - /// - /// Represents inline Image. - /// - internal class RunImage : Run - { - public Image Image { get { return image; } } - override public float Width - { - get + /// + /// + /// + /// + /// + public Image Load(string src) { - if (Image == null) return base.Width; - return Image.Width; + CacheItem item = null; + if (String.IsNullOrEmpty(src)) + item = new CacheItem(); + else + lock (locker) + { + if (items == null) + items = new Dictionary(); + else + if (items.ContainsKey(src)) + return items[src].Image; + item = new CacheItem(); + if (Validate(src)) + { + try + { + if (src.StartsWith("data:")) + { + item.Set(src.Substring(src.IndexOf("base64,") + "base64,".Length)); + } + else + item.Set(Client.DownloadData(src)); + } + catch + { + item.Set(""); + } + } + items[src] = item; + Serialized = false; + } + item.Src = src; + return item.Image; } - } - override public float Top - { - get + /// + /// Set CacheItem by src + /// + /// Src attribute from img tag + /// CacheItem + /// + public CacheItem Set(string src, CacheItem item) { - float baseLine = 0; - if (Style.BaseLine == BaseLine.Subscript) - baseLine += FontLineHeight * 0.45f; - else if (Style.BaseLine == BaseLine.Superscript) - baseLine -= FontLineHeight * 0.15f; - return word.Top + word.line.CalcBaseLine() - CurrentBaseLine + baseLine; + if (String.IsNullOrEmpty(src)) + return new CacheItem(); + lock (locker) + { + if (items == null) + items = new Dictionary(); + if (!Validate(src)) + item = new CacheItem(); + items[src] = item; + Serialized = false; + } + item.Src = src; + return item; } - } - override public float CurrentBaseLine - { - get + /// + /// Validate src attribute from image + /// + /// Src attribute from img tag + /// return true if src is valid + public bool Validate(string src) { - if (Image == null) return base.CurrentBaseLine; - return Image.Height; + if (String.IsNullOrEmpty(src)) + return false; + src = src.ToLower(); + if (src.StartsWith("http://")) + return true; + if (src.StartsWith("https://")) + return true; + if (src.StartsWith("data:") && src.IndexOf("base64,") > 0) + return true; + return false; } - } - override public float Height - { - get - { - if (Image == null) return base.Height; - return Image.Height + word.line.CalcUnderBaseLine(); - } - } + #endregion Public Methods - private Image image; + #region Internal Methods - override public void Draw() - { - if (Image == null) + static internal Image Load(InlineImageCache cache, string src) { - base.Draw(); - return; + LoadEventArgs args = new LoadEventArgs(cache, src); + if (BeforeLoad != null) BeforeLoad(null, args); + Image result = null; + if (!args.Handled) result = cache.Load(src); + args.Handled = false; + if (AfterLoad != null) AfterLoad(null, args); + if (args.Handled) + return cache.Get(src).Image; + return result; } - Renderer.Graphics.DrawImage(Image, Left, Top);// (FText, font, brush, Left, Top, Renderer.Format); - } - public static Bitmap ResizeImage(Image image, float scale) - { - int width = (int)(image.Width * scale); - int height = (int)(image.Height * scale); - if (width == 0) width = 1; - if (height == 0) height = 1; - Rectangle destRect = new Rectangle(0, 0, width, height); - Bitmap destImage = new Bitmap(width, height); + #endregion Internal Methods - destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); + #region Public Constructors - using (Graphics graphics = Graphics.FromImage(destImage)) + /// + public InlineImageCache() { - graphics.CompositingMode = CompositingMode.SourceCopy; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - using (System.Drawing.Imaging.ImageAttributes wrapMode = new System.Drawing.Imaging.ImageAttributes()) - { - wrapMode.SetWrapMode(WrapMode.TileFlipXY); - graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode); - } + locker = new object(); + client = null; } - return destImage; - } + /// + /// + /// + ~InlineImageCache() + { + Dispose(false); + } - public RunImage(string src, string text, StyleDescriptor style, Word word) : base(text, style, word) - { - underBaseLine = 0; - image = ResizeImage(InlineImageCache.Load(Renderer.Cache,src), Renderer.Scale); - } - } - } - - - /// - /// Standard text renderer uses standard DrawString method to draw text. It also supports: - /// - text rotation; - /// - fonts with non-standard width ratio. - /// In case your text is justified, or contains html tags, use the - /// class instead. - /// - internal class StandardTextRenderer - { - public static void Draw(string text, Graphics g, Font font, Brush brush, Pen outlinePen, - RectangleF rect, StringFormat format, int angle, float widthRatio) - { - GraphicsState state = g.Save(); - g.SetClip(rect, CombineMode.Intersect); - g.TranslateTransform(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); - g.RotateTransform(angle); - rect.X = -rect.Width / 2; - rect.Y = -rect.Height / 2; - - if ((angle >= 90 && angle < 180) || (angle >= 270 && angle < 360)) - rect = new RectangleF(rect.Y, rect.X, rect.Height, rect.Width); - - g.ScaleTransform(widthRatio, 1); - rect.X /= widthRatio; - rect.Width /= widthRatio; - - if (outlinePen == null) - { - g.DrawString(text, font, brush, rect, format); - } - else - { - GraphicsPath path = new GraphicsPath(); - path.AddString(text, font.FontFamily, Convert.ToInt32(font.Style), g.DpiY * font.Size / 72, rect, format); - g.FillPath(brush, path); - g.DrawPath(outlinePen, path); - } - - g.Restore(state); - } - } + #endregion Public Constructors - /// - /// Cache for rendering img tags in textobject. - /// You can use only HTTP[s] protocol with absolute urls. - /// - public class InlineImageCache : IDisposable - { -#region Private Fields + #region Public Classes - private WebClient client; + /// + /// Item of image cache Dictionary + /// + public class CacheItem : IDisposable + { + #region Private Fields - private Dictionary items; + private string base64; - private bool serialized; + private bool error; - private object locker; + private Image image; -#endregion Private Fields + //private int FId; + private string src; -#region Public Properties + private byte[] stream; - /// - /// Is serialized - /// - public bool Serialized { get { return serialized; } set { serialized = value; } } + #endregion Private Fields -#endregion Public Properties + #region Public Properties -#region Private Properties + /// + /// Get Base64 string + /// + public string Base64 + { + get + { + try + {//For strange img tag + if (base64 != null) + return base64; + if (stream != null) + { + base64 = Convert.ToBase64String(stream); + return base64; + } + if (image != null) + { + using (MemoryStream ms = new MemoryStream()) + { + image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + ms.Flush(); + stream = ms.ToArray(); + } + base64 = Convert.ToBase64String(stream); + return base64; + } + } + catch { } + GetErrorImage(); + return ""; + } + } - /// - /// Get or set WebClient for downloading imgs by url - /// - private WebClient Client - { - get - { - if (client == null) - { - client = new WebClient(); - } - return client; - } - set - { - client = value; - } - } + /// + /// Return true if has some error with Image + /// + public bool Error + { + get { return error; } + } -#endregion Private Properties + /// + /// Get Image + /// + public Image Image + { + get + { + try + {//for strange img tag + if (image != null) + return image; + if (stream != null) + { + MemoryStream ms = new MemoryStream(stream); + image = Bitmap.FromStream(ms); + return image; + } + if (base64 != null) + { + this.stream = Convert.FromBase64String(base64); + MemoryStream ms = new MemoryStream(stream); + image = Bitmap.FromStream(ms); + return image; + } + } + catch { } + return GetErrorImage(); + } + } -#region Public Events + /// + /// Get byte array + /// + public byte[] Stream + { + get + { + if (stream != null) return stream; + if (base64 != null) + { + stream = Convert.FromBase64String(base64); + return stream; + } + if (image != null) + { + using (MemoryStream ms = new MemoryStream()) + { + image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + ms.Flush(); + stream = ms.ToArray(); + } + return stream; + } + return new byte[0]; + } + } - /// - /// Occurs before image load - /// - public static event EventHandler AfterLoad; - /// - /// Occurs after image load - /// - public static event EventHandler BeforeLoad; + #endregion Public Properties -#endregion Public Events + #region Internal Properties -#region Public Methods + internal string Src + { + get { return src; } + set { src = value; } + } - /// - /// Enumerates all values - /// - /// - public IEnumerable AllItems() - { - List list = new List(); - lock (locker) - { - if (items != null) - { - foreach (KeyValuePair item in items) - { - item.Value.Src = item.Key; - list.Add(item.Value); - } - } - } - return list; - } + #endregion Internal Properties - /// - /// Return CacheItem by src - /// - /// Src attribute from img tag - /// - public CacheItem Get(string src) - { - CacheItem item = null; - if (!Validate(src)) - item = new CacheItem(); - if (String.IsNullOrEmpty(src)) - return item; - lock (locker) - { - if (items == null) - { - items = new Dictionary(); - if (item == null) - item = new CacheItem(); - items[src] = item; - Serialized = false; - } - if (items.ContainsKey(src)) - return items[src]; - } - return item; - } + #region Public Methods - /// - /// - /// - /// - /// - public Image Load(string src) - { - CacheItem item = null; - if (String.IsNullOrEmpty(src)) - item = new CacheItem(); - else - lock (locker) - { - if (items == null) - items = new Dictionary(); - else - if (items.ContainsKey(src)) - return items[src].Image; - item = new CacheItem(); - if (Validate(src)) - { - try - { - if (src.StartsWith("data:")) - { - item.Set(src.Substring(src.IndexOf("base64,") + "base64,".Length)); - } - else - item.Set(Client.DownloadData(src)); - } - catch - { - item.Set(""); - } - } - items[src] = item; - Serialized = false; - } - item.Src = src; - return item.Image; - } + /// + /// Return error image and set true to error property + /// + /// + public Image GetErrorImage() + { + error = true; + base64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAFdJREFUOE9jbGlq+c9AIqipq2GEawEZQAo4dvgYqoXD0QAGhv9ATyKCBY1PXBjANKEbBjSWOANA9mPRDBImzgCKXECVMMCTsojzwtAzAOQvUjCJmRe/cgDt6ZAkZx23LwAAAABJRU5ErkJggg=="; + src = "data:image/png;base64," + base64; + stream = Convert.FromBase64String(base64); + using (MemoryStream ms = new MemoryStream(stream)) + image = Bitmap.FromStream(ms); + return image; + } - /// - /// Set CacheItem by src - /// - /// Src attribute from img tag - /// CacheItem - /// - public CacheItem Set(string src, CacheItem item) - { - if (String.IsNullOrEmpty(src)) - return new CacheItem(); - lock (locker) - { - if (items == null) - items = new Dictionary(); - if (!Validate(src)) - item = new CacheItem(); - items[src] = item; - Serialized = false; - } - item.Src = src; - return item; - } + /// + /// Set value for cache item + /// + /// Image encoded base64 string + public void Set(string base64) + { + this.base64 = base64; + image = null; + stream = null; + } - /// - /// Validate src attribute from image - /// - /// Src attribute from img tag - /// return true if src is valid - public bool Validate(string src) - { - if (String.IsNullOrEmpty(src)) - return false; - src = src.ToLower(); - if (src.StartsWith("http://")) - return true; - if (src.StartsWith("https://")) - return true; - if (src.StartsWith("data:") && src.IndexOf("base64,") > 0) - return true; - return false; - } + /// + /// Set value for cache item + /// + /// Image + public void Set(Image img) + { + base64 = null; + image = img; + stream = null; + } -#endregion Public Methods + /// + /// Set value for cache item + /// + /// Image + public void Set(byte[] arr) + { + base64 = null; + image = null; + stream = arr; + } -#region Internal Methods + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls - static internal Image Load(InlineImageCache cache, string src) - { - LoadEventArgs args = new LoadEventArgs(cache, src); - if (BeforeLoad != null) BeforeLoad(null, args); - Image result = null; - if (!args.Handled) result = cache.Load(src); - args.Handled = false; - if (AfterLoad != null) AfterLoad(null, args); - if (args.Handled) - return cache.Get(src).Image; - return result; - } + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + if (image != null) + { + image.Dispose(); + image = null; + } + // TODO: dispose managed state (managed objects). + } -#endregion Internal Methods + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. -#region Public Constructors + disposedValue = true; + } + } - /// - public InlineImageCache() - { - locker = new object(); - client = null; - } + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~CacheItem() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } - /// - /// - /// - ~InlineImageCache() - { - Dispose(false); - } + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion -#endregion Public Constructors + #endregion Public Methods + } -#region Public Classes + /// + /// WebClientEventArgs + /// + public class LoadEventArgs : EventArgs + { + #region Private Fields - /// - /// Item of image cache Dictionary - /// - public class CacheItem : IDisposable - { -#region Private Fields + private InlineImageCache cache; + private bool handled; + private string source; - private string base64; + #endregion Private Fields - private bool error; + #region Public Properties - private Image image; + /// + /// Gets a cache + /// + public InlineImageCache Cache { get { return cache; } } - //private int FId; - private string src; + /// + /// Gets or sets a value indicating whether the event was handled. + /// + public bool Handled { get { return handled; } set { handled = value; } } - private byte[] stream; + /// + /// Gets or sets a url from src attribue of img tag + /// + public string Source { get { return source; } set { source = value; } } -#endregion Private Fields + #endregion Public Properties -#region Public Properties + #region Internal Constructors - /// - /// Get Base64 string - /// - public string Base64 - { - get - { - try - {//For strange img tag - if (base64 != null) - return base64; - if (stream != null) - { - base64 = Convert.ToBase64String(stream); - return base64; - } - if (image != null) - { - using (MemoryStream ms = new MemoryStream()) - { - image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); - ms.Flush(); - stream = ms.ToArray(); - } - base64 = Convert.ToBase64String(stream); - return base64; - } - } - catch { } - GetErrorImage(); - return ""; - } - } - - /// - /// Return true if has some error with Image - /// - public bool Error - { - get { return error; } - } - - /// - /// Get Image - /// - public Image Image - { - get - { - try - {//for strange img tag - if (image != null) - return image; - if (stream != null) - { - MemoryStream ms = new MemoryStream(stream); - image = Bitmap.FromStream(ms); - return image; - } - if (base64 != null) - { - this.stream = Convert.FromBase64String(base64); - MemoryStream ms = new MemoryStream(stream); - image = Bitmap.FromStream(ms); - return image; - } - } - catch { } - return GetErrorImage(); - } - } - - /// - /// Get byte array - /// - public byte[] Stream - { - get - { - if (stream != null) return stream; - if (base64 != null) - { - stream = Convert.FromBase64String(base64); - return stream; - } - if (image != null) - { - using (MemoryStream ms = new MemoryStream()) - { - image.Save(ms, System.Drawing.Imaging.ImageFormat.Png); - ms.Flush(); - stream = ms.ToArray(); - } - return stream; - } - return new byte[0]; - } - } - -#endregion Public Properties - -#region Internal Properties - - internal string Src - { - get { return src; } - set { src = value; } - } - -#endregion Internal Properties - -#region Public Methods - - /// - /// Return error image and set true to error property - /// - /// - public Image GetErrorImage() - { - error = true; - base64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAFdJREFUOE9jbGlq+c9AIqipq2GEawEZQAo4dvgYqoXD0QAGhv9ATyKCBY1PXBjANKEbBjSWOANA9mPRDBImzgCKXECVMMCTsojzwtAzAOQvUjCJmRe/cgDt6ZAkZx23LwAAAABJRU5ErkJggg=="; - src = "data:image/png;base64," + base64; - stream = Convert.FromBase64String(base64); - using (MemoryStream ms = new MemoryStream(stream)) - image = Bitmap.FromStream(ms); - return image; - } - - /// - /// Set value for cache item - /// - /// Image encoded base64 string - public void Set(string base64) - { - this.base64 = base64; - image = null; - stream = null; - } - - /// - /// Set value for cache item - /// - /// Image - public void Set(Image img) - { - base64 = null; - image = img; - stream = null; - } - - /// - /// Set value for cache item - /// - /// Image - public void Set(byte[] arr) - { - base64 = null; - image = null; - stream = arr; - } - -#region IDisposable Support - private bool disposedValue = false; // To detect redundant calls - - /// - /// - /// - /// - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - if (image != null) + internal LoadEventArgs(InlineImageCache c, string src) { - image.Dispose(); - image = null; + cache = c; + source = src; + handled = false; } - // TODO: dispose managed state (managed objects). - } - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; + #endregion Internal Constructors } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~CacheItem() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } -#endregion - -#endregion Public Methods - } - - /// - /// WebClientEventArgs - /// - public class LoadEventArgs : EventArgs - { -#region Private Fields - - private InlineImageCache cache; - private bool handled; - private string source; - -#endregion Private Fields -#region Public Properties + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls - /// - /// Gets a cache - /// - public InlineImageCache Cache { get { return cache; } } - - /// - /// Gets or sets a value indicating whether the event was handled. - /// - public bool Handled { get { return handled; } set { handled = value; } } - - /// - /// Gets or sets a url from src attribue of img tag - /// - public string Source { get { return source; } set { source = value; } } - -#endregion Public Properties - -#region Internal Constructors - - internal LoadEventArgs(InlineImageCache c, string src) - { - cache = c; - source = src; - handled = false; - } + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + } -#endregion Internal Constructors - } + if (this.items != null) + { + Dictionary items = this.items; + this.items = null; + foreach (CacheItem item in items.Values) + item.Dispose(); + } -#region IDisposable Support - private bool disposedValue = false; // To detect redundant calls + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. - /// - /// - /// - /// - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects). + disposedValue = true; + } } - if (this.items != null) + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~InlineImageCache() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + // This code added to correctly implement the disposable pattern. + public void Dispose() { - Dictionary items = this.items; - this.items = null; - foreach (CacheItem item in items.Values) - item.Dispose(); + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); } + #endregion - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~InlineImageCache() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); + #endregion Public Classes } -#endregion - -#endregion Public Classes - } } \ No newline at end of file diff --git a/FastReport.Base/Utils/Zip.cs b/FastReport.Base/Utils/Zip.cs index 39fa944e..74ca4ec6 100644 --- a/FastReport.Base/Utils/Zip.cs +++ b/FastReport.Base/Utils/Zip.cs @@ -152,7 +152,7 @@ public void SaveToStream(Stream Stream) { if (fileList[i].Disk) { - ZipFile.LocalFileHeader.FileName = fileList[i].Name.Replace(rootFolder + "\\", ""); + ZipFile.LocalFileHeader.FileName = fileList[i].Name.Replace(rootFolder + Path.DirectorySeparatorChar, ""); using (FileStream file = new FileStream(fileList[i].Name, FileMode.Open)) AddStreamToZip(file, ZipFile); } diff --git a/FastReport.Core.Web/Application/Dialog/WebComboBox.cs b/FastReport.Core.Web/Application/Dialog/WebComboBox.cs index dcc01131..da29c50e 100644 --- a/FastReport.Core.Web/Application/Dialog/WebComboBox.cs +++ b/FastReport.Core.Web/Application/Dialog/WebComboBox.cs @@ -22,6 +22,8 @@ private string GetComboBoxHtml(ComboBoxControl control) } else { + if (control.SelectedIndex == -1) + control.SelectedIndex = 0; control.SelectedItem = control.Items[control.SelectedIndex]; control.Text = control.SelectedItem.ToString(); } diff --git a/FastReport.Core.Web/Application/WebReportDesigner.cs b/FastReport.Core.Web/Application/WebReportDesigner.cs index 00c08399..6542a6d7 100644 --- a/FastReport.Core.Web/Application/WebReportDesigner.cs +++ b/FastReport.Core.Web/Application/WebReportDesigner.cs @@ -45,6 +45,16 @@ partial class WebReport /// Callback method for saving an edited report by Online Designer /// Params: reportID, report file name, report, out - message ///
+ /// + /// webReport.DesignerSaveMethod = (string reportID, string filename, string report) => + /// { + /// string webRootPath = _hostingEnvironment.WebRootPath; + /// string pathToSave = Path.Combine(webRootPath, filename); + /// System.IO.File.WriteAllTextAsync(pathToSave, report); + /// + /// return "OK"; + /// }; + /// public Func DesignerSaveMethod { get; set; } /// @@ -119,18 +129,19 @@ internal IActionResult DesignerSaveReport(HttpContext context) // paste restricted back in report before save Report.LoadFromString(PasteRestricted(reportString)); - SaveDesignedReportEventArgs e = new SaveDesignedReportEventArgs(); - e.Stream = new MemoryStream(); - Report.Save(e.Stream); - e.Stream.Position = 0; - OnSaveDesignedReport(e); + if (SaveDesignedReport != null) + { + SaveDesignedReportEventArgs e = new SaveDesignedReportEventArgs(); + e.Stream = new MemoryStream(); + Report.Save(e.Stream); + e.Stream.Position = 0; + OnSaveDesignedReport(e); + } if (!DesignerSaveCallBack.IsNullOrWhiteSpace()) { string report = Report.SaveToString(); - var reportName = (!String.IsNullOrEmpty(Report.ReportInfo.Name) ? - Report.ReportInfo.Name : Path.GetFileNameWithoutExtension(Report.FileName)); - string reportFileName = $"{reportName}.frx"; + string reportFileName = ReportFileName; UriBuilder uri = new UriBuilder { @@ -586,7 +597,7 @@ string PasteRestricted(string xmlString) // context.Response.Write(sb.ToString()); //} - string GetPOSTReport(HttpContext context) + internal string GetPOSTReport(HttpContext context) { string requestString = ""; using (TextReader textReader = new StreamReader(context.Request.Body)) diff --git a/FastReport.Core.Web/Controllers/DesignerController.cs b/FastReport.Core.Web/Controllers/DesignerController.cs index f701fc39..3d1e230b 100644 --- a/FastReport.Core.Web/Controllers/DesignerController.cs +++ b/FastReport.Core.Web/Controllers/DesignerController.cs @@ -40,7 +40,7 @@ public DesignerController() : base() { // save by using a Func - string report = webReport.Report.SaveToString(); + string report = webReport.GetPOSTReport(Context); string msg = string.Empty; int code = 200; try diff --git a/FastReport.Core.Web/FastReport.OpenSource.Web.csproj b/FastReport.Core.Web/FastReport.OpenSource.Web.csproj index 531e3bd5..f16598c0 100644 --- a/FastReport.Core.Web/FastReport.OpenSource.Web.csproj +++ b/FastReport.Core.Web/FastReport.OpenSource.Web.csproj @@ -7,28 +7,22 @@ Fast Reports Inc. Fast Reports Inc. FastReport.Web - FastReport.Net is a full-featured reporting solution for .Net Core 2.0. -Various report objects will allow your report to look exactly how you want it to: 13 types of bands, 25 types of barcodes, table object, diagram, maps, shapes, line, PolyLine, Polygon and many more. -FastReport.Net supports export to various popular formats, such as PDF/A, Excel, Word, Open Office, HTML, CSV, Json, XAML, ZPL, etc. - Fast Reports Inc. https://www.fast-report.com/en/product/fast-report-net - https://www.fast-report.com/download/images/frlogo-big.png + reporting, reports, pdf, html, mvc, core true Debug;Release;Demo - - The full version of the package is available in FastReport.Net Professional at https://www.fast-report.com/en/fast-report-net-editions-compare/ - https://github.com/FastReports/FastReport/blob/master/LICENSE.md OPENSOURCE; FastReport.OpenSource.Web ../FastReport.OpenSource.snk - FastReport Open Source is an open source reporting solution for .Net Core 2.x and .Net Framework 4.x. + FastReport Open Source is an open source reporting solution for .Net Core and .Net Framework 4.x. Various report objects will allow your report to look exactly how you want it to: 13 types of bands, 25 types of barcodes, table object, shapes, line, PolyLine, Polygon and many more. https://github.com/FastReports/FastReport + frlogo-big.png @@ -54,6 +48,10 @@ Various report objects will allow your report to look exactly how you want it to + + True + + @@ -77,6 +75,6 @@ Various report objects will allow your report to look exactly how you want it to - + diff --git a/FastReport.Core.Web/FastReport.Web.csproj b/FastReport.Core.Web/FastReport.Web.csproj index 258998c9..33da3800 100644 --- a/FastReport.Core.Web/FastReport.Web.csproj +++ b/FastReport.Core.Web/FastReport.Web.csproj @@ -14,7 +14,8 @@ FastReport.Net supports export to various popular formats, such as PDF/A, Excel, Fast Reports Inc. https://www.fast-report.com/en/product/fast-report-net - https://www.fast-report.com/download/images/frlogo-big.png + + frlogo-big.png reporting, reports, pdf, html, mvc, core true Debug;Release;Demo @@ -25,9 +26,9 @@ FastReport.Net supports export to various popular formats, such as PDF/A, Excel, FastReport.Web ../FastReport.Net.snk - FastReport.Net is a full-featured reporting solution for .Net Core. + FastReport.Core is a full-featured reporting solution for .Net Core. Various report objects will allow your report to look exactly how you want it to: 13 types of bands, 25 types of barcodes, table object, diagram, maps, shapes, line, PolyLine, Polygon and many more. -FastReport.Net supports export to various popular formats, such as PDF/A, Excel, Word, Open Office, HTML, CSV, Json, XAML, ZPL, etc. +FastReport.Core supports export to various popular formats, such as PDF/A, Excel, Word, Open Office, HTML, CSV, Json, XAML, ZPL, etc. $(DemoDescription) @@ -55,6 +56,10 @@ $(DemoDescription) + + True + + diff --git a/FastReport.Core.Web/Templates/style.cs b/FastReport.Core.Web/Templates/style.cs index d8066131..ff49b4fe 100644 --- a/FastReport.Core.Web/Templates/style.cs +++ b/FastReport.Core.Web/Templates/style.cs @@ -16,6 +16,11 @@ string template_style() => $@" position: relative; }} +.{template_FR}-container * {{ + box-sizing: content-box; + -moz-box-sizing: content-box; +}} + .{template_FR}-body {{ display: flex; overflow: hidden; diff --git a/FastReport.OpenSource.sln b/FastReport.OpenSource.sln index e4082537..e9fe0c67 100644 --- a/FastReport.OpenSource.sln +++ b/FastReport.OpenSource.sln @@ -41,11 +41,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.OpenSource.Expor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.OpenSource.Export.PdfSimple.Tests", "Extras\OpenSource\FastReport.OpenSource.Export.PdfSimple\FastReport.OpenSource.Export.PdfSimple.Tests\FastReport.OpenSource.Export.PdfSimple.Tests.csproj", "{01DA6786-77D4-4457-BF33-E204F0C9E155}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReportBuilder", "ReportBuilder", "{669B319D-9D1F-49B3-A719-03C9E9ABD692}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.ReportBuilder", "Extras\ReportBuilder\FastReport.ReportBuilder\FastReport.ReportBuilder.csproj", "{ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastReport.ReportBuilder.UnitTest", "Extras\ReportBuilder\FastReport.ReportBuilder.UnitTest\FastReport.ReportBuilder.UnitTest.csproj", "{17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastReport.OpenSource.Data.ClickHouse", "Extras\Core\FastReport.Data\FastReport.ClickHouse\FastReport.ClickHouse\FastReport.OpenSource.Data.ClickHouse.csproj", "{BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -216,26 +212,16 @@ Global {01DA6786-77D4-4457-BF33-E204F0C9E155}.Release|Any CPU.Build.0 = Release|Any CPU {01DA6786-77D4-4457-BF33-E204F0C9E155}.WinForms|Any CPU.ActiveCfg = Debug|Any CPU {01DA6786-77D4-4457-BF33-E204F0C9E155}.WinForms|Any CPU.Build.0 = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Academic|Any CPU.ActiveCfg = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Academic|Any CPU.Build.0 = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Demo|Any CPU.ActiveCfg = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Demo|Any CPU.Build.0 = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.Release|Any CPU.Build.0 = Release|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.WinForms|Any CPU.ActiveCfg = Debug|Any CPU - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A}.WinForms|Any CPU.Build.0 = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Academic|Any CPU.ActiveCfg = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Academic|Any CPU.Build.0 = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Demo|Any CPU.ActiveCfg = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Demo|Any CPU.Build.0 = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.Release|Any CPU.Build.0 = Release|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.WinForms|Any CPU.ActiveCfg = Debug|Any CPU - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE}.WinForms|Any CPU.Build.0 = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Academic|Any CPU.ActiveCfg = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Academic|Any CPU.Build.0 = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Demo|Any CPU.ActiveCfg = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Demo|Any CPU.Build.0 = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.Release|Any CPU.Build.0 = Release|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.WinForms|Any CPU.ActiveCfg = Debug|Any CPU + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35}.WinForms|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -255,9 +241,7 @@ Global {FD840207-C565-4E98-8118-D7EF7F183D7C} = {8E9E75EB-2ABC-4CC1-8AA0-C11DEE54A152} {09E87462-E2B8-4B8F-AE41-55A8584471F9} = {CCF32DAC-85D9-43D4-A4A7-72626A13D806} {01DA6786-77D4-4457-BF33-E204F0C9E155} = {CCF32DAC-85D9-43D4-A4A7-72626A13D806} - {669B319D-9D1F-49B3-A719-03C9E9ABD692} = {CCF32DAC-85D9-43D4-A4A7-72626A13D806} - {ABFBDAC5-AC9E-44B4-9201-FE8BFBD5DB0A} = {669B319D-9D1F-49B3-A719-03C9E9ABD692} - {17CE85B1-B182-4BEA-90A8-F8D110B1CFCE} = {669B319D-9D1F-49B3-A719-03C9E9ABD692} + {BDB7D5CB-8DD2-48DB-BA81-FDDC88135B35} = {898AF8DC-11C3-4640-B60C-5D8CBF2F29CF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DFBB1F5B-F03E-4BCB-AFEE-A45A2C4D5BB8} diff --git a/FastReport.OpenSource/Code/AssemblyDescriptor.Core.cs b/FastReport.OpenSource/Code/AssemblyDescriptor.Core.cs index fc56aa9b..504715d8 100644 --- a/FastReport.OpenSource/Code/AssemblyDescriptor.Core.cs +++ b/FastReport.OpenSource/Code/AssemblyDescriptor.Core.cs @@ -9,24 +9,12 @@ namespace FastReport.Code { partial class AssemblyDescriptor { - private void ErrorMsg(CompilerError ce, int number) - { - - } + partial void ErrorMsg(CompilerError ce, int number); - private void ErrorMsg(string str, CompilerError ce) - { + partial void ErrorMsg(string str, CompilerError ce); - } + partial void ErrorMsg(string str); - private void ErrorMsg(string str) - { - - } - - private void ReviewReferencedAssemblies(StringCollection referencedAssemblies) - { - - } + partial void ReviewReferencedAssemblies(StringCollection referencedAssemblies); } } diff --git a/FastReport.OpenSource/Code/ExpressionDescriptor.Core.cs b/FastReport.OpenSource/Code/ExpressionDescriptor.Core.cs index 7d27f156..b34c15d8 100644 --- a/FastReport.OpenSource/Code/ExpressionDescriptor.Core.cs +++ b/FastReport.OpenSource/Code/ExpressionDescriptor.Core.cs @@ -6,7 +6,7 @@ internal class ExpressionDescriptor { private string methodName; private MethodInfo methodInfo; - private AssemblyDescriptor assembly; + private readonly AssemblyDescriptor assembly; public string MethodName { diff --git a/FastReport.OpenSource/CrossVeiw/CrossViewHelper.Core.cs b/FastReport.OpenSource/CrossVeiw/CrossViewHelper.Core.cs index f06ac539..100fb30b 100644 --- a/FastReport.OpenSource/CrossVeiw/CrossViewHelper.Core.cs +++ b/FastReport.OpenSource/CrossVeiw/CrossViewHelper.Core.cs @@ -5,9 +5,6 @@ partial class CrossViewHelper /// /// Does nothing /// - private void OnProgressInternal() - { - - } + partial void OnProgressInternal(); } } diff --git a/FastReport.OpenSource/CrossVeiw/CrossViewObject.Core.cs b/FastReport.OpenSource/CrossVeiw/CrossViewObject.Core.cs index 93544b0e..d3825680 100644 --- a/FastReport.OpenSource/CrossVeiw/CrossViewObject.Core.cs +++ b/FastReport.OpenSource/CrossVeiw/CrossViewObject.Core.cs @@ -6,9 +6,6 @@ partial class CrossViewObject /// Does nothing /// /// - private void RefreshTemplate(bool flag) - { - - } + partial void RefreshTemplate(bool flag); } } diff --git a/FastReport.OpenSource/Data/DataConnectionBase.Core.cs b/FastReport.OpenSource/Data/DataConnectionBase.Core.cs index 207197af..a7697562 100644 --- a/FastReport.OpenSource/Data/DataConnectionBase.Core.cs +++ b/FastReport.OpenSource/Data/DataConnectionBase.Core.cs @@ -11,9 +11,7 @@ partial class DataConnectionBase /// Does nothing /// /// - private void FilterTables(List tableNames) - { - } + partial void FilterTables(List tableNames); /// /// Does nothing @@ -36,9 +34,7 @@ private bool ShouldNotDispose(DbConnection connection) /// /// Does nothing /// - private void ShowLoginForm(string lastConnectionString) - { - } + partial void ShowLoginForm(string lastConnectionString); #endregion Private Methods } diff --git a/FastReport.OpenSource/Data/TableDataSource.Core.cs b/FastReport.OpenSource/Data/TableDataSource.Core.cs index 4b59cd88..51d16a6e 100644 --- a/FastReport.OpenSource/Data/TableDataSource.Core.cs +++ b/FastReport.OpenSource/Data/TableDataSource.Core.cs @@ -7,9 +7,7 @@ partial class TableDataSource /// /// Does nothing /// - private void TryToLoadData() - { - } + partial void TryToLoadData(); #endregion Private Methods } diff --git a/FastReport.OpenSource/Dialog/DialogPage.Core.cs b/FastReport.OpenSource/Dialog/DialogPage.Core.cs index 86c33de1..6955fb69 100644 --- a/FastReport.OpenSource/Dialog/DialogPage.Core.cs +++ b/FastReport.OpenSource/Dialog/DialogPage.Core.cs @@ -7,9 +7,7 @@ partial class DialogPage /// /// Does nothing /// - private void ResetFormBitmap() - { - } + partial void ResetFormBitmap(); #endregion Private Methods } diff --git a/FastReport.OpenSource/Engine/ReportEngine.Core.cs b/FastReport.OpenSource/Engine/ReportEngine.Core.cs index 3e3b6094..e5a4139e 100644 --- a/FastReport.OpenSource/Engine/ReportEngine.Core.cs +++ b/FastReport.OpenSource/Engine/ReportEngine.Core.cs @@ -2,8 +2,6 @@ { partial class ReportEngine { - private void ShowProgress() - { - } + partial void ShowProgress(); } } \ No newline at end of file diff --git a/FastReport.OpenSource/Engine/ReportEngine.Rich.cs b/FastReport.OpenSource/Engine/ReportEngine.Rich.cs new file mode 100644 index 00000000..38e731b1 --- /dev/null +++ b/FastReport.OpenSource/Engine/ReportEngine.Rich.cs @@ -0,0 +1,15 @@ +namespace FastReport.Engine +{ + partial class ReportEngine + { + private void InitializePages() + { + for (int i = 0; i < Report.Pages.Count; i++) + { + ReportPage page = Report.Pages[i] as ReportPage; + if (page != null) + PreparedPages.AddSourcePage(page); + } + } + } +} \ No newline at end of file diff --git a/FastReport.OpenSource/Export/ExportBase.Core.cs b/FastReport.OpenSource/Export/ExportBase.Core.cs index 1f0292bc..993cc71d 100644 --- a/FastReport.OpenSource/Export/ExportBase.Core.cs +++ b/FastReport.OpenSource/Export/ExportBase.Core.cs @@ -10,10 +10,7 @@ partial class ExportBase /// Does nothing /// /// - private void ShowPerformance(int int0) - { - - } + partial void ShowPerformance(int int0); protected ReportPage GetOverlayPage(ReportPage page) { diff --git a/FastReport.OpenSource/Export/Html/HTMLExport.Core.cs b/FastReport.OpenSource/Export/Html/HTMLExport.Core.cs index ea57ec80..6efff202 100644 --- a/FastReport.OpenSource/Export/Html/HTMLExport.Core.cs +++ b/FastReport.OpenSource/Export/Html/HTMLExport.Core.cs @@ -27,10 +27,7 @@ private bool HasExtendedExport(ReportComponentBase obj) return false; } - private void ExtendExport(FastString Page, ReportComponentBase obj, FastString text) - { - - } + partial void ExtendExport(FastString Page, ReportComponentBase obj, FastString text); private class ExportIEMStyle { diff --git a/FastReport.OpenSource/FastReport.OpenSource.csproj b/FastReport.OpenSource/FastReport.OpenSource.csproj index dba9a72d..013d4a2b 100644 --- a/FastReport.OpenSource/FastReport.OpenSource.csproj +++ b/FastReport.OpenSource/FastReport.OpenSource.csproj @@ -14,6 +14,7 @@ true + frlogo-big.png ../FastReport.OpenSource.snk true Fast Reports Inc. @@ -23,13 +24,13 @@ Fast Reports Inc. FastReport FastReport.OpenSource - https://www.fast-report.com/download/images/frlogo-big.png + reporting, reports, pdf, html, mvc, core 1.0.0 Debug;Release FastReport FastReport - FastReport Open Source is an open source reporting solution for .Net Core 2.x and .Net Framework 4.x. + FastReport Open Source is an open source reporting solution for .Net Core and .Net Framework 4.x. Various report objects will allow your report to look exactly how you want it to: 13 types of bands, 25 types of barcodes, table object, shapes, line, PolyLine, Polygon and many more. https://github.com/FastReports/FastReport @@ -74,4 +75,12 @@ Various report objects will allow your report to look exactly how you want it to
+ + + + True + + + + \ No newline at end of file diff --git a/FastReport.OpenSource/HtmlObject.Core.cs b/FastReport.OpenSource/HtmlObject.Core.cs index cfaa9776..92ff7aa3 100644 --- a/FastReport.OpenSource/HtmlObject.Core.cs +++ b/FastReport.OpenSource/HtmlObject.Core.cs @@ -8,9 +8,6 @@ partial class HtmlObject /// Does nothing /// /// - private void DrawDesign(FRPaintEventArgs e) - { - - } + partial void DrawDesign(FRPaintEventArgs e); } } diff --git a/FastReport.OpenSource/Matrix/MatrixObject.Core.cs b/FastReport.OpenSource/Matrix/MatrixObject.Core.cs index a22bec13..42018c8d 100644 --- a/FastReport.OpenSource/Matrix/MatrixObject.Core.cs +++ b/FastReport.OpenSource/Matrix/MatrixObject.Core.cs @@ -7,17 +7,13 @@ partial class MatrixObject /// /// Does nothing /// - private void InitDesign() - { - } + partial void InitDesign(); /// /// Does nothing /// /// - private void RefreshTemplate(bool flag) - { - } + partial void RefreshTemplate(bool flag); #endregion Private Methods } diff --git a/FastReport.OpenSource/PictureObjectBase.Core.cs b/FastReport.OpenSource/PictureObjectBase.Core.cs index 8bbf337b..acf18220 100644 --- a/FastReport.OpenSource/PictureObjectBase.Core.cs +++ b/FastReport.OpenSource/PictureObjectBase.Core.cs @@ -19,9 +19,6 @@ protected void DrawErrorImage(Graphics g, FRPaintEventArgs e) /// Does nothing /// /// - private void DrawDesign(FRPaintEventArgs e) - { - - } + partial void DrawDesign(FRPaintEventArgs e); } } diff --git a/FastReport.OpenSource/PolyLineObject.Core.cs b/FastReport.OpenSource/PolyLineObject.Core.cs index e0897091..a120465d 100644 --- a/FastReport.OpenSource/PolyLineObject.Core.cs +++ b/FastReport.OpenSource/PolyLineObject.Core.cs @@ -10,24 +10,18 @@ partial class PolyLineObject /// Does nothing /// /// - private void DrawDesign0(FRPaintEventArgs e) - { - } + partial void DrawDesign0(FRPaintEventArgs e); /// /// Does nothing /// /// - private void DrawDesign1(FRPaintEventArgs e) - { - } + partial void DrawDesign1(FRPaintEventArgs e); /// /// Does nothing /// - private void InitDesign() - { - } + partial void InitDesign(); #endregion Private Methods } diff --git a/FastReport.OpenSource/Preview/SourcePages.OpenSource.cs b/FastReport.OpenSource/Preview/SourcePages.OpenSource.cs new file mode 100644 index 00000000..7f778141 --- /dev/null +++ b/FastReport.OpenSource/Preview/SourcePages.OpenSource.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; +using FastReport.Utils; + +namespace FastReport.Preview +{ + internal partial class SourcePages + { + private Base RichObjectTranslation(Base source, Base parent) + { + return null; + } + } +} diff --git a/FastReport.OpenSource/Report.Core.cs b/FastReport.OpenSource/Report.Core.cs index 039e11ba..60b78886 100644 --- a/FastReport.OpenSource/Report.Core.cs +++ b/FastReport.OpenSource/Report.Core.cs @@ -27,34 +27,22 @@ private string ShowPaswordForm(string password) /// /// /// - private void SerializeDesign(FRWriter writer, Report report) - { - - } + partial void SerializeDesign(FRWriter writer, Report report); /// /// Does nothing /// - private void InitDesign() - { - - } + partial void InitDesign(); /// /// Does nothing /// - private void ClearDesign() - { - - } + partial void ClearDesign(); /// /// Does nothing /// - private void DisposeDesign() - { - - } + partial void DisposeDesign(); #endregion Private Methods } diff --git a/FastReport.OpenSource/ReportPage.Core.cs b/FastReport.OpenSource/ReportPage.Core.cs index 2e362b7b..36c775e7 100644 --- a/FastReport.OpenSource/ReportPage.Core.cs +++ b/FastReport.OpenSource/ReportPage.Core.cs @@ -10,25 +10,19 @@ partial class ReportPage /// Does nothing /// /// - private void AssignPreview(ReportPage reportPage) - { - } + partial void AssignPreview(ReportPage reportPage); /// /// Does nothing /// - private void InitPreview() - { - } + partial void InitPreview(); /// /// Does nothing /// /// /// - private void WritePreview(FRWriter writer, ReportPage reportPage) - { - } + partial void WritePreview(FRWriter writer, ReportPage reportPage); #endregion Private Methods } diff --git a/FastReport.OpenSource/ReportSettings.Core.cs b/FastReport.OpenSource/ReportSettings.Core.cs index 40e3c8eb..4b6c04ea 100644 --- a/FastReport.OpenSource/ReportSettings.Core.cs +++ b/FastReport.OpenSource/ReportSettings.Core.cs @@ -30,7 +30,7 @@ internal void OnProgress(Report report, string str) /// internal void OnProgress(Report report, string str, int int1, int int2) { - + } /// diff --git a/FastReport.OpenSource/Table/TableBase.Core.cs b/FastReport.OpenSource/Table/TableBase.Core.cs index 4d0e9f12..49c71032 100644 --- a/FastReport.OpenSource/Table/TableBase.Core.cs +++ b/FastReport.OpenSource/Table/TableBase.Core.cs @@ -10,41 +10,31 @@ partial class TableBase /// Does nothing /// /// - private void DrawDesign(FRPaintEventArgs e) - { - } + partial void DrawDesign(FRPaintEventArgs e); /// /// Does nothing /// /// - private void DrawDesign_Borders(FRPaintEventArgs e) - { - } + partial void DrawDesign_Borders(FRPaintEventArgs e); /// /// Does nothing /// /// - private void DrawDesign_BordersRtl(FRPaintEventArgs e) - { - } + partial void DrawDesign_BordersRtl(FRPaintEventArgs e); /// /// Does nothing /// /// - private void DrawDesign_SelectedCells(FRPaintEventArgs e) - { - } + partial void DrawDesign_SelectedCells(FRPaintEventArgs e); /// /// Does nothing /// /// - private void DrawDesign_SelectedCellsRtl(FRPaintEventArgs e) - { - } + partial void DrawDesign_SelectedCellsRtl(FRPaintEventArgs e); #endregion Private Methods } diff --git a/FastReport.OpenSource/TextObject.Core.cs b/FastReport.OpenSource/TextObject.Core.cs index d9ef5159..0b9eda76 100644 --- a/FastReport.OpenSource/TextObject.Core.cs +++ b/FastReport.OpenSource/TextObject.Core.cs @@ -8,9 +8,6 @@ partial class TextObject /// Does nothing /// /// - private void DrawDesign(FRPaintEventArgs e) - { - - } + partial void DrawDesign(FRPaintEventArgs e); } } diff --git a/FastReport.OpenSource/Utils/Config.Core.cs b/FastReport.OpenSource/Utils/Config.Core.cs index 6cfd354b..59d42931 100644 --- a/FastReport.OpenSource/Utils/Config.Core.cs +++ b/FastReport.OpenSource/Utils/Config.Core.cs @@ -41,28 +41,18 @@ public static bool WebMode /// /// Does nothing /// - private static void RestoreUIStyle() - { - } + static partial void RestoreUIStyle(); /// /// Does nothing /// - private static void SaveUIStyle() - { - } + static partial void SaveUIStyle(); - private static void RestorePreviewSettings() - { - } + static partial void RestorePreviewSettings(); - private static void SavePreviewSettings() - { - } + static partial void SavePreviewSettings(); - private static void SaveExportOptions() - { - } + static partial void SaveExportOptions(); private static void RestoreExportOptions() { diff --git a/FastReport.OpenSource/Utils/ExportsOptions.Core.cs b/FastReport.OpenSource/Utils/ExportsOptions.Core.cs index fde26b2e..410259f8 100644 --- a/FastReport.OpenSource/Utils/ExportsOptions.Core.cs +++ b/FastReport.OpenSource/Utils/ExportsOptions.Core.cs @@ -8,8 +8,8 @@ namespace FastReport.Utils { partial class ExportsOptions { - private void SaveOptions() { } + partial void SaveOptions(); - private void RestoreOptions() { } + partial void RestoreOptions(); } } diff --git a/FastReport.OpenSource/Utils/RegisteredObjects.Core.cs b/FastReport.OpenSource/Utils/RegisteredObjects.Core.cs index e5048902..fab52ed7 100644 --- a/FastReport.OpenSource/Utils/RegisteredObjects.Core.cs +++ b/FastReport.OpenSource/Utils/RegisteredObjects.Core.cs @@ -15,9 +15,7 @@ partial class ObjectInfo /// /// /// - private void UpdateDesign(object obj, Bitmap image, int imageIndex, string text, int flags, bool multiInsert) - { - } + partial void UpdateDesign(object obj, Bitmap image, int imageIndex, string text, int flags, bool multiInsert); /// /// Does nothing. @@ -29,9 +27,7 @@ private void UpdateDesign(object obj, Bitmap image, int imageIndex, string text, /// /// /// - private void UpdateDesign(object obj, Bitmap image, int imageIndex, int ButtonIndex, string text, int flags, bool multiInsert) - { - } + partial void UpdateDesign(object obj, Bitmap image, int imageIndex, int ButtonIndex, string text, int flags, bool multiInsert); #endregion Private Methods } diff --git a/FastReport/Resources/en.xml b/FastReport/Resources/en.xml index d4fc4002..540cf6f8 100644 --- a/FastReport/Resources/en.xml +++ b/FastReport/Resources/en.xml @@ -60,6 +60,8 @@ + + @@ -459,6 +461,7 @@ + @@ -1838,6 +1841,7 @@ + @@ -1850,6 +1854,7 @@ + @@ -1960,9 +1965,7 @@ - + @@ -1985,12 +1988,10 @@ - + + (for example, for Rosreestr payments)"/> @@ -2031,6 +2032,7 @@ + @@ -2048,8 +2050,8 @@ - - + + @@ -2381,6 +2383,9 @@ + + + diff --git a/Localization/Arabic.frl b/Localization/Arabic.frl new file mode 100644 index 00000000..435b29f4 --- /dev/null +++ b/Localization/Arabic.frl @@ -0,0 +1,2258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +