diff --git a/.gitmodules b/.gitmodules index 4a6ad6b2..1a060e37 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "NReflect"] path = libs/NReflect - url = https://github.com/gbaychev/NReflect.git + url = https://github.com/syntacs/NReflect.git diff --git a/libs/NReflect b/libs/NReflect index 4f8a594d..73c78264 160000 --- a/libs/NReflect +++ b/libs/NReflect @@ -1 +1 @@ -Subproject commit 4f8a594d3245b4981544089fefd8726625b6cc2e +Subproject commit 73c78264b98fba24f40ec3854ae94718b9c5a11c diff --git a/src/AssemblyImport.Tests/AssemblyImport.Tests.csproj b/src/AssemblyImport.Tests/AssemblyImport.Tests.csproj index 0ca73e95..04085018 100644 --- a/src/AssemblyImport.Tests/AssemblyImport.Tests.csproj +++ b/src/AssemblyImport.Tests/AssemblyImport.Tests.csproj @@ -1,272 +1,14 @@ - - - - - - + - Debug - AnyCPU - {8C178134-FD78-42B6-AA06-8372E49C8287} - Library - Properties - AssemblyImport.Tests - AssemblyImport.Tests - v4.7.2 - 512 - true - - - + net5.0-windows7.0 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - false - bin\Release\ - TRACE - prompt - 4 - - - false - - - - - ..\packages\Microsoft.CodeAnalysis.Common.3.2.1\lib\netstandard2.0\Microsoft.CodeAnalysis.dll - - - ..\packages\Microsoft.CodeAnalysis.CSharp.3.2.1\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll - - - ..\..\libs\NReflect\NReflect\bin\$(Configuration)\NReflect.dll - - - ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll - - - ..\packages\Shouldly.3.0.2\lib\net451\Shouldly.dll - - - - ..\packages\System.AppContext.4.3.0\lib\net463\System.AppContext.dll - - - ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll - - - ..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll - - - - ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll - True - True - - - - ..\packages\System.Diagnostics.FileVersionInfo.4.3.0\lib\net46\System.Diagnostics.FileVersionInfo.dll - True - True - - - ..\packages\System.Diagnostics.StackTrace.4.3.0\lib\net46\System.Diagnostics.StackTrace.dll - True - True - - - ..\packages\System.IO.4.3.0\lib\net462\System.IO.dll - True - True - - - ..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll - True - True - - - ..\packages\System.IO.FileSystem.4.3.0\lib\net46\System.IO.FileSystem.dll - True - True - - - ..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll - True - True - - - ..\packages\System.Linq.4.3.0\lib\net463\System.Linq.dll - True - True - - - ..\packages\System.Linq.Expressions.4.3.0\lib\net463\System.Linq.Expressions.dll - True - True - - - ..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll - - - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - - ..\packages\System.Reflection.4.3.0\lib\net462\System.Reflection.dll - True - True - - - ..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll - - - ..\packages\System.Runtime.4.3.1\ref\net462\System.Runtime.dll - True - - - ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Runtime.Extensions.4.3.1\lib\net462\System.Runtime.Extensions.dll - True - True - - - ..\packages\System.Runtime.InteropServices.4.3.0\lib\net463\System.Runtime.InteropServices.dll - True - True - - - ..\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net463\System.Security.Cryptography.Algorithms.dll - True - True - - - ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll - True - True - - - ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll - True - True - - - ..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll - True - True - - - ..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll - - - ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll - - - ..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll - True - True - - - ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll - - - - - - - - - ..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll - True - True - - - ..\packages\System.Xml.XmlDocument.4.3.0\lib\net46\System.Xml.XmlDocument.dll - True - True - - - ..\packages\System.Xml.XPath.4.3.0\lib\net46\System.Xml.XPath.dll - True - True - - - ..\packages\System.Xml.XPath.XDocument.4.3.0\lib\net46\System.Xml.XPath.XDocument.dll - True - True - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - {88605d35-9e31-47cc-b6a2-45e6381d52b9} - AssemblyImport - - - {8cf10505-3c2e-4e45-ab90-21613237b412} - Core - - - {755b2714-62db-419c-9097-49c08439e7e3} - CSharp - - - {F52A7548-E60A-485B-9C84-1D2871416DB7} - DiagramEditor - - - - Designer - - - Designer - + + - - + + + - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/src/AssemblyImport.Tests/Properties/AssemblyInfo.cs b/src/AssemblyImport.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index b8a30316..00000000 --- a/src/AssemblyImport.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AssemblyImport.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AssemblyImport.Tests")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8c178134-fd78-42b6-aa06-8372e49c8287")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/AssemblyImport/AssemblyImport.build b/src/AssemblyImport/AssemblyImport.build deleted file mode 100644 index 213aae29..00000000 --- a/src/AssemblyImport/AssemblyImport.build +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/AssemblyImport/AssemblyImport.csproj b/src/AssemblyImport/AssemblyImport.csproj index fcfbd58b..806bd0fc 100644 --- a/src/AssemblyImport/AssemblyImport.csproj +++ b/src/AssemblyImport/AssemblyImport.csproj @@ -1,177 +1,18 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {88605D35-9E31-47CC-B6A2-45E6381D52B9} - Library - Properties - NClass.AssemblyImport - AssemblyImport - 512 - - - - - v4.7.2 - - - - - v4.5 - - - true - full - false - ..\GUI\bin\Debug\Plugins\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - ..\GUI\bin\Release\Plugins\ - TRACE - prompt - 4 - false - - - - ..\..\libs\NReflect\NReflect\bin\$(Configuration)\NReflect.dll - - - - - - - - - - - - - - - - - Component - - - Component - - - - - - - - - - - - - - - Form - - - ImportSettingsForm.cs - - - True - True - Strings.resx - - - - - - - True - True - Resources.resx - - - True - True - Settings.settings - - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {755B2714-62DB-419C-9097-49C08439E7E3} - CSharp - - - {F52A7548-E60A-485B-9C84-1D2871416DB7} - DiagramEditor - - - {A2918CE1-6894-4FC9-A864-4651769D0274} - GUI - - - - - ImportSettingsForm.cs - Designer - - - Strings.de.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.Designer.cs - Designer - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - - - - - - - - - - - - - - - - - + + + net5.0-windows7.0 + true + NClass.AssemblyImport + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AssemblyImport/Declarations/NRArgumentListDeclaration.cs b/src/AssemblyImport/Declarations/NRArgumentListDeclaration.cs index 32aed635..3c32ce54 100644 --- a/src/AssemblyImport/Declarations/NRArgumentListDeclaration.cs +++ b/src/AssemblyImport/Declarations/NRArgumentListDeclaration.cs @@ -1,4 +1,5 @@ -using NClass.CSharp; +using NClass.Core; +using NClass.CSharp; using NReflect.NRParameters; using System.Collections; using System.Collections.Generic; diff --git a/src/AssemblyImport/NETImport.cs b/src/AssemblyImport/NETImport.cs index 4fd2bd84..3b969af5 100644 --- a/src/AssemblyImport/NETImport.cs +++ b/src/AssemblyImport/NETImport.cs @@ -103,7 +103,7 @@ public NETImport(ClassDiagram diagram, ImportSettings settings) /// /// The file name and path of the assembly to import. /// True, if the import was successful. - public bool ImportAssembly(string fileName, bool useNewAppDomain = true) + public bool ImportAssembly(string fileName, bool useNewAppDomain = false) { if (string.IsNullOrEmpty(fileName)) { diff --git a/src/AssemblyImport/Properties/AssemblyInfo.cs b/src/AssemblyImport/Properties/AssemblyInfo.cs deleted file mode 100644 index 7a79d21b..00000000 --- a/src/AssemblyImport/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AssemblyImport")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AssemblyImport")] -[assembly: AssemblyCopyright("Copyright © Malte Ried 2011")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8e637e0d-5d60-4a39-8bee-406564453b98")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/src/CSharp/CSharp.build b/src/CSharp/CSharp.build deleted file mode 100644 index e9a46c99..00000000 --- a/src/CSharp/CSharp.build +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/CSharp/CSharp.csproj b/src/CSharp/CSharp.csproj index d1a234e8..edf32f86 100644 --- a/src/CSharp/CSharp.csproj +++ b/src/CSharp/CSharp.csproj @@ -1,151 +1,11 @@ - - + - Debug - AnyCPU - 9.0.21022 - 2.0 - {755B2714-62DB-419C-9097-49C08439E7E3} - Library - Properties - NClass.CSharp + net5.0-windows7.0 NClass.CSharp - - - 3.5 - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false + NClass.CSharp - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - + + - - - \ No newline at end of file diff --git a/src/CSharp/Members/CSharpEnumValue.cs b/src/CSharp/Members/CSharpEnumValue.cs index 7104712e..651fc1b7 100644 --- a/src/CSharp/Members/CSharpEnumValue.cs +++ b/src/CSharp/Members/CSharpEnumValue.cs @@ -25,10 +25,10 @@ internal sealed class CSharpEnumValue : EnumValue // [= value] const string EnumNamePattern = "(?" + CSharpLanguage.NamePattern + ")"; const string EnumItemPattern = @"^\s*" + EnumNamePattern + - @"(\s*=\s*(?\d+))?\s*$"; + @"(\s*=\s*(?-?\d+))?\s*$"; static Regex enumItemRegex = new Regex(EnumItemPattern, RegexOptions.ExplicitCapture); - + int? initValue; /// @@ -50,44 +50,48 @@ public override void InitFromString(string declaration) { Match match = enumItemRegex.Match(declaration); - try { + try + { RaiseChangedEvent = false; - if (match.Success) { + if (match.Success) + { Group nameGroup = match.Groups["name"]; Group valueGroup = match.Groups["value"]; Name = nameGroup.Value; - if (valueGroup.Success) - { - int intValue; - if(int.TryParse(valueGroup.Value, out intValue)) - initValue = intValue; - else - initValue = null; - } - else - { - initValue = null; - } + if (valueGroup.Success) + { + int intValue; + if (int.TryParse(valueGroup.Value, out intValue)) + initValue = intValue; + else + initValue = null; + } + else + { + initValue = null; + } } - else { + else + { throw new BadSyntaxException(Strings.ErrorInvalidDeclaration); } } - finally { + finally + { RaiseChangedEvent = true; } } public override string GetDeclaration() { - if (InitValue == null) + if (InitValue == null) return Name; - return Name + " = " + InitValue; + return Name + " = " + InitValue; } - protected override EnumValue Clone() + protected override EnumValue Clone() { return new CSharpEnumValue(GetDeclaration()); } diff --git a/src/CSharp/Properties/AssemblyInfo.cs b/src/CSharp/Properties/AssemblyInfo.cs deleted file mode 100644 index 48de42d2..00000000 --- a/src/CSharp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.CSharp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.CSharp")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f8c58560-477c-4144-a806-f7ecaa2ed698")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] -[assembly: InternalsVisibleTo("NClass.Tests")] diff --git a/src/CodeGenerator/CodeGenerator.build b/src/CodeGenerator/CodeGenerator.build deleted file mode 100644 index 7b374efb..00000000 --- a/src/CodeGenerator/CodeGenerator.build +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/CodeGenerator/CodeGenerator.csproj b/src/CodeGenerator/CodeGenerator.csproj index 3ea70fa2..9d45eb9e 100644 --- a/src/CodeGenerator/CodeGenerator.csproj +++ b/src/CodeGenerator/CodeGenerator.csproj @@ -1,171 +1,13 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {BE60D62D-E757-44F4-A342-5388742F5986} - Library - Properties - NClass.CodeGenerator - NClass.CodeGenerator - - - - - 3.5 - - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - - - - - - - - - - - - - - - - Form - - - Dialog.cs - - - - - - - - - - True - True - Resources.resx - - - - True - True - Settings.settings - - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {755B2714-62DB-419C-9097-49C08439E7E3} - CSharp - - - {255565B1-3246-4184-9F71-19178FE0BA65} - Java - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - - - - - Designer - Dialog.cs - - - Designer - ResXFileCodeGenerator - Resources.Designer.cs - - - - - - Always - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - Always - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - + + + net5.0-windows7.0 + true + NClass.CodeGenerator + NClass.CodeGenerator + + + + + + \ No newline at end of file diff --git a/src/CodeGenerator/Properties/AssemblyInfo.cs b/src/CodeGenerator/Properties/AssemblyInfo.cs deleted file mode 100644 index 69c65260..00000000 --- a/src/CodeGenerator/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.CodeGenerator")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.CodeGenerator")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("cc533464-1f37-4b5f-9ac3-c5519e42c031")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] diff --git a/src/Core/Core.build b/src/Core/Core.build deleted file mode 100644 index 9f3eed00..00000000 --- a/src/Core/Core.build +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index 9f73c7c0..83a3e00c 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -1,214 +1,10 @@ - - + - Debug - AnyCPU - 9.0.30729 - 2.0 - {8CF10505-3C2E-4E45-AB90-21613237B412} - Library - Properties - NClass.Core + net5.0-windows7.0 NClass.Core - - - - - 3.5 - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AllRules.ruleset - false - - - none - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false + NClass.Core - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - + - - - \ No newline at end of file diff --git a/src/Core/Models/ClassModel.cs b/src/Core/Models/ClassModel.cs index 0bd503b5..bb51af5b 100644 --- a/src/Core/Models/ClassModel.cs +++ b/src/Core/Models/ClassModel.cs @@ -16,7 +16,6 @@ using System; using System.IO; using System.Linq; -using System.Net.Configuration; using System.Xml; using NClass.Translations; diff --git a/src/Core/Properties/AssemblyInfo.cs b/src/Core/Properties/AssemblyInfo.cs deleted file mode 100644 index 37b821ac..00000000 --- a/src/Core/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.Core")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.Core")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2018-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("b225fe77-8f13-43ed-aa2f-4b3c3d5a8a16")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] -[assembly: InternalsVisibleTo("NClass.Tests")] \ No newline at end of file diff --git a/src/DiagramEditor/DiagramEditor.build b/src/DiagramEditor/DiagramEditor.build deleted file mode 100644 index ccbbbe58..00000000 --- a/src/DiagramEditor/DiagramEditor.build +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/DiagramEditor/DiagramEditor.csproj b/src/DiagramEditor/DiagramEditor.csproj index ba085c8a..4ba80ef3 100644 --- a/src/DiagramEditor/DiagramEditor.csproj +++ b/src/DiagramEditor/DiagramEditor.csproj @@ -1,459 +1,13 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {F52A7548-E60A-485B-9C84-1D2871416DB7} - Library - Properties - NClass.DiagramEditor - NClass.DiagramEditor - - - 3.5 - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UserControl - - - PackageEditor.cs - - - - - - - - - - - - - - - - - - - - - UserControl - - - - UserControl - - - CommentEditor.cs - - - - - UserControl - - - DesignerHelperWindow.cs - - - UserControl - - - ShapeNameEditor.cs - - - - - - Form - - - AssociationDialog.cs - - - Form - - - Form - - - EditCommentDialog.cs - - - Form - - - Form - - - Form - - - ListDialog.cs - - - Form - - - MembersDialog.cs - - - Form - - - - DiagramPrintDialog.cs - - - Form - - - TreeDialog.cs - - - UserControl - - - UserControl - - - UserControl - - - UserControl - - - UserControl - - - - - - - - - - - - UserControl - - - UserControl - - - CompositeTypeEditor.cs - - - UserControl - - - DelegateEditor.cs - - - UserControl - - - EnumEditor.cs - - - UserControl - - - ItemEditor.cs - - - UserControl - - - MemberEditor.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Settings.settings - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - - UserControl - - - Canvas.cs - - - - - - - - - - - - - - - - - - - - - - - - - Designer - Canvas.cs - - - AssociationDialog.cs - Designer - - - EditCommentDialog.cs - Designer - - - ListDialog.cs - Designer - - - MembersDialog.cs - Designer - - - DiagramPrintDialog.cs - Designer - - - TreeDialog.cs - Designer - - - CommentEditor.cs - Designer - - - CompositeTypeEditor.cs - Designer - - - DelegateEditor.cs - Designer - - - EnumEditor.cs - Designer - - - ItemEditor.cs - Designer - - - MemberEditor.cs - Designer - - - PackageEditor.cs - - - ShapeNameEditor.cs - - - Designer - PublicResXFileCodeGenerator - Resources.Designer.cs - - - - - {BE60D62D-E757-44F4-A342-5388742F5986} - CodeGenerator - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - - - - - - PublicSettingsSingleFileGenerator - Settings.Designer.cs - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - - - - - - - - + + + net5.0-windows7.0 + true + NClass.DiagramEditor + NClass.DiagramEditor + + + + + + \ No newline at end of file diff --git a/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.Designer.cs b/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.Designer.cs deleted file mode 100644 index 5137b1da..00000000 --- a/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.Designer.cs +++ /dev/null @@ -1,39 +0,0 @@ -#if DEBUG -namespace NClass.DiagramEditor.Diagrams.Editors -{ - partial class DesignerHelperWindow - { - /// - /// 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() - { - components = new System.ComponentModel.Container(); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.cs b/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.cs deleted file mode 100644 index 61ab51e1..00000000 --- a/src/DiagramEditor/Diagrams/Editors/DesignerHelperWindow.cs +++ /dev/null @@ -1,50 +0,0 @@ -// NClass - Free class diagram editor -// Copyright (C) 2006-2009 Balazs Tihanyi -// Copyright (C) 2020 Georgi Baychev -// -// This program is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free Software -// Foundation; either version 3 of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but WITHOUT -// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along with -// this program; if not, write to the Free Software Foundation, Inc., -// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -#if DEBUG -namespace NClass.DiagramEditor.Diagrams.Editors -{ - /// - /// The only purpose of this class is to have a non-abstract - /// class, from which the other editor windows can derive from, - /// so that the editor window can be shown in the Visual Studio - /// designer. For more information check this link out: - /// http://stackoverflow.com/questions/481305/the-designer-must-create-an-instance-of-cannot-because-the-type-is-declared-ab - /// - public partial class DesignerHelperWindow : EditorWindow - { - public DesignerHelperWindow() - { - InitializeComponent(); - } - - internal override void Init(DiagramElement element) - { - throw new System.NotImplementedException(); - } - - internal override void Relocate(DiagramElement element) - { - throw new System.NotImplementedException(); - } - - public override void ValidateData() - { - throw new System.NotImplementedException(); - } - } -} -#endif \ No newline at end of file diff --git a/src/DiagramEditor/Properties/AssemblyInfo.cs b/src/DiagramEditor/Properties/AssemblyInfo.cs deleted file mode 100644 index 98b54b59..00000000 --- a/src/DiagramEditor/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.DiagramEditor")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.DiagramEditor")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("766a9cc7-546a-42d3-9fc3-e21f6027fc94")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] diff --git a/src/GUI/GUI.build b/src/GUI/GUI.build deleted file mode 100644 index b86a42a8..00000000 --- a/src/GUI/GUI.build +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj index fb965ff3..ff5e7852 100644 --- a/src/GUI/GUI.csproj +++ b/src/GUI/GUI.csproj @@ -1,294 +1,22 @@ - - + - Debug - AnyCPU - 9.0.21022 - 2.0 - {A2918CE1-6894-4FC9-A864-4651769D0274} - WinExe - Properties - NClass.GUI - NClass - NClass.GUI.Program - - - - - 3.5 - - - false - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true + net5.0-windows7.0 + true + WinExe + NClass + NClass.GUI.Program + NClass.GUI - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - x86 - false - - - none - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - - - - ..\packages\CommonMark.NET.0.15.1\lib\net45\CommonMark.dll - - - - ..\packages\Octokit.0.32.0\lib\net45\Octokit.dll - - - - - - - - - - - - Form - - - Form - - - UpdateDialog.cs - - - - - - - - ModelView.cs - - - - - Component - - - Form - - - AboutDialog.cs - - - Form - - - MainForm.cs - - - Form - - - OptionsDialog.cs - - - Component - - - - - Designer - AboutDialog.cs - - - Designer - OptionsDialog.cs - - - UpdateDialog.cs - - - Designer - MainForm.cs - - - ModelView.cs - Designer - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - TabBar.cs - Designer - - - TabbedWindow.cs - Designer - - - - - True - Resources.resx - True - - - - Settings.settings - True - True - - - Component - - - TabBar.cs - - - UserControl - - - TabbedWindow.cs - - - Component - - - UndoRedoListView.cs - - - - True - True - WindowSettings.settings - - - - Component - - - - - {BE60D62D-E757-44F4-A342-5388742F5986} - CodeGenerator - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {755B2714-62DB-419C-9097-49C08439E7E3} - CSharp - - - {F52A7548-E60A-485B-9C84-1D2871416DB7} - DiagramEditor - - - {255565B1-3246-4184-9F71-19178FE0BA65} - Java - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - - - - - - - PublicSettingsSingleFileGenerator - Settings.Designer.cs - - - PreserveNewest - styles\Visual Studio Class Designer %28ClearType%29.dst - - - PreserveNewest - styles\Visual Studio Class Designer.dst - - - SettingsSingleFileGenerator - WindowSettings.Designer.cs - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - + + - + + + + + + - - \ No newline at end of file diff --git a/src/GUI/MainForm.cs b/src/GUI/MainForm.cs index 9ab88845..ae746c3d 100644 --- a/src/GUI/MainForm.cs +++ b/src/GUI/MainForm.cs @@ -29,6 +29,7 @@ using NClass.DiagramEditor.UseCaseDiagram; using NClass.GUI.Dialogs; using NClass.Translations; +using System.Linq; namespace NClass.GUI { @@ -119,17 +120,16 @@ private async void MainForm_Load(object sender, EventArgs e) { LoadPlugins(); LoadWindowSettings(); - await UpdatesChecker.CheckForUpdates(true); + //await UpdatesChecker.CheckForUpdates(true); } private void LoadPlugins() { try { - string pluginsPath = Path.Combine(Application.StartupPath, "Plugins"); - if (!Directory.Exists(pluginsPath)) - return; + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + string pluginsPath = Path.Combine(Application.StartupPath, "Plugins"); DirectoryInfo directory = new DirectoryInfo(pluginsPath); foreach (FileInfo file in directory.GetFiles("*.dll")) @@ -137,6 +137,7 @@ private void LoadPlugins() Assembly assembly = Assembly.LoadFile(file.FullName); LoadPlugin(assembly); } + AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve; } catch (Exception ex) { @@ -156,6 +157,32 @@ private void LoadPlugins() } } } + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + // Ignore missing resources + if (args.Name.Contains(".resources")) + return null; + + // check for assemblies already loaded + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); + if (assembly != null) + return assembly; + + // Try to load by filename - split out the filename of the full assembly name + // and append the base path of the original assembly (ie. look in the same dir) + string filename = args.Name.Split(',')[0] + ".dll".ToLower(); + + string asmFile = Path.Combine(@".\Plugins\", filename); + + try + { + return System.Reflection.Assembly.LoadFrom(asmFile); + } + catch (Exception ex) + { + return null; + } + } private void LoadPlugin(Assembly assembly) { diff --git a/src/GUI/Properties/AssemblyInfo.cs b/src/GUI/Properties/AssemblyInfo.cs deleted file mode 100644 index dc36c056..00000000 --- a/src/GUI/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.GUI")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.GUI")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("7eed6ae1-8ebb-4ef7-8ddf-eea1336df9fa")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.1")] -[assembly: AssemblyInformationalVersion("branch-and-git-hash-here")] diff --git a/src/GUI/app.config b/src/GUI/app.config index eef3c073..7ff82174 100644 --- a/src/GUI/app.config +++ b/src/GUI/app.config @@ -12,7 +12,7 @@ CSharp - default + de True diff --git a/src/Java/Java.build b/src/Java/Java.build deleted file mode 100644 index 79d89716..00000000 --- a/src/Java/Java.build +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/Java/Java.cs b/src/Java/Java.cs deleted file mode 100644 index 99bce255..00000000 Binary files a/src/Java/Java.cs and /dev/null differ diff --git a/src/Java/Java.csproj b/src/Java/Java.csproj index 509e0f8a..da189d9a 100644 --- a/src/Java/Java.csproj +++ b/src/Java/Java.csproj @@ -1,137 +1,11 @@ - - + - Debug - AnyCPU - 9.0.21022 - 2.0 - {255565B1-3246-4184-9F71-19178FE0BA65} - Library - Properties - NClass.Java + net5.0-windows7.0 NClass.Java - - - 3.5 - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false + NClass.Java - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Translations - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - + + - - - \ No newline at end of file diff --git a/src/Java/Properties/AssemblyInfo.cs b/src/Java/Properties/AssemblyInfo.cs deleted file mode 100644 index fd0d416a..00000000 --- a/src/Java/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.Java")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.Java")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("10d4d937-b89e-4ad7-9278-5933ceb2e645")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] -[assembly: InternalsVisibleTo("NClass.Tests")] \ No newline at end of file diff --git a/src/NClass.sln b/src/NClass.sln index bd46311d..50e6eb3c 100644 --- a/src/NClass.sln +++ b/src/NClass.sln @@ -3,38 +3,35 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30406.217 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "GUI\GUI.csproj", "{A2918CE1-6894-4FC9-A864-4651769D0274}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GUI", "GUI\GUI.csproj", "{A2918CE1-6894-4FC9-A864-4651769D0274}" ProjectSection(ProjectDependencies) = postProject {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} = {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core", "Core\Core.csproj", "{8CF10505-3C2E-4E45-AB90-21613237B412}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{8CF10505-3C2E-4E45-AB90-21613237B412}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Translations", "Translations\Translations.csproj", "{B3B7D798-3D52-47F0-B1A7-A91BC5FE184F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Translations", "Translations\Translations.csproj", "{B3B7D798-3D52-47F0-B1A7-A91BC5FE184F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{BE60D62D-E757-44F4-A342-5388742F5986}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeGenerator", "CodeGenerator\CodeGenerator.csproj", "{BE60D62D-E757-44F4-A342-5388742F5986}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyImport", "AssemblyImport\AssemblyImport.csproj", "{88605D35-9E31-47CC-B6A2-45E6381D52B9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssemblyImport", "AssemblyImport\AssemblyImport.csproj", "{88605D35-9E31-47CC-B6A2-45E6381D52B9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp", "CSharp\CSharp.csproj", "{755B2714-62DB-419C-9097-49C08439E7E3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharp", "CSharp\CSharp.csproj", "{755B2714-62DB-419C-9097-49C08439E7E3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Java", "Java\Java.csproj", "{255565B1-3246-4184-9F71-19178FE0BA65}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java", "Java\Java.csproj", "{255565B1-3246-4184-9F71-19178FE0BA65}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiagramEditor", "DiagramEditor\DiagramEditor.csproj", "{F52A7548-E60A-485B-9C84-1D2871416DB7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DiagramEditor", "DiagramEditor\DiagramEditor.csproj", "{F52A7548-E60A-485B-9C84-1D2871416DB7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDFExport", "PDFExport\PDFExport.csproj", "{53E9092C-848E-4D6A-9E8A-2ECD71BAEF04}" EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Installer", "Installer\Installer.wixproj", "{2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}" - ProjectSection(ProjectDependencies) = postProject - {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04} = {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04} - {88605D35-9E31-47CC-B6A2-45E6381D52B9} = {88605D35-9E31-47CC-B6A2-45E6381D52B9} - {A2918CE1-6894-4FC9-A864-4651769D0274} = {A2918CE1-6894-4FC9-A864-4651769D0274} - EndProjectSection -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssemblyImport.Tests", "AssemblyImport.Tests\AssemblyImport.Tests.csproj", "{8C178134-FD78-42B6-AA06-8372E49C8287}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B3B77F1B-1309-4E44-BE70-E9BAE9F511DB}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NReflect", "..\libs\NReflect\NReflect\NReflect.csproj", "{760AF36D-9463-491D-9FD0-B9C429D8030C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PdfSharp-gdi.50", "PDFsharp\src\PdfSharp-gdi\PdfSharp-gdi.50.csproj", "{121AB3DF-6CBD-444E-A209-1245CA23BC80}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -135,17 +132,6 @@ Global {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04}.Release|Mixed Platforms.Build.0 = Release|Any CPU {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04}.Release|x86.ActiveCfg = Release|Any CPU - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Debug|Any CPU.ActiveCfg = Debug|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Debug|Mixed Platforms.Build.0 = Debug|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Debug|x86.ActiveCfg = Debug|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Debug|x86.Build.0 = Debug|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|Any CPU.ActiveCfg = Release|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|Any CPU.Build.0 = Release|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|Mixed Platforms.ActiveCfg = Release|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|Mixed Platforms.Build.0 = Release|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|x86.ActiveCfg = Release|x86 - {2B8FF6C7-EF77-438E-B1B2-1037DEFE5D7B}.Release|x86.Build.0 = Release|x86 {8C178134-FD78-42B6-AA06-8372E49C8287}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8C178134-FD78-42B6-AA06-8372E49C8287}.Debug|Any CPU.Build.0 = Debug|Any CPU {8C178134-FD78-42B6-AA06-8372E49C8287}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -170,6 +156,30 @@ Global {B3B77F1B-1309-4E44-BE70-E9BAE9F511DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU {B3B77F1B-1309-4E44-BE70-E9BAE9F511DB}.Release|x86.ActiveCfg = Release|Any CPU {B3B77F1B-1309-4E44-BE70-E9BAE9F511DB}.Release|x86.Build.0 = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|x86.ActiveCfg = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Debug|x86.Build.0 = Debug|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|Any CPU.Build.0 = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|x86.ActiveCfg = Release|Any CPU + {760AF36D-9463-491D-9FD0-B9C429D8030C}.Release|x86.Build.0 = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|x86.ActiveCfg = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Debug|x86.Build.0 = Debug|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|Any CPU.Build.0 = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|x86.ActiveCfg = Release|Any CPU + {121AB3DF-6CBD-444E-A209-1245CA23BC80}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/PDFExport/NClassFontResolver.cs b/src/PDFExport/NClassFontResolver.cs index cdbe9246..df2a4905 100644 --- a/src/PDFExport/NClassFontResolver.cs +++ b/src/PDFExport/NClassFontResolver.cs @@ -28,7 +28,7 @@ namespace PDFExport /// Implements the 'glorious' mechanism of /// PdfSharp to resolve fonts /// - public class NClassFontResolver : IFontResolver + public class NClassFontResolver : IFontResolver { /// /// General information about font, including filename @@ -96,7 +96,6 @@ public override string ToString() /// Contains the actual binary data of the font files /// private readonly IDictionary fontDataCache; - /// /// Ok, since fonts under linux/unix is such clusterfuck, /// the only hope is to call fc-list from fontconfig @@ -108,7 +107,7 @@ private IDictionary EnumerateUnixFonts() { var p = new Process { - StartInfo = {FileName = "fc-list", RedirectStandardOutput = true, UseShellExecute = false} + StartInfo = { FileName = "fc-list", RedirectStandardOutput = true, UseShellExecute = false } }; var cache = new Dictionary(25); diff --git a/src/PDFExport/PDFExport.build b/src/PDFExport/PDFExport.build deleted file mode 100644 index 4ea75770..00000000 --- a/src/PDFExport/PDFExport.build +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/PDFExport/PDFExport.csproj b/src/PDFExport/PDFExport.csproj index 2e3581cc..8a8048f9 100644 --- a/src/PDFExport/PDFExport.csproj +++ b/src/PDFExport/PDFExport.csproj @@ -1,200 +1,17 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {53E9092C-848E-4D6A-9E8A-2ECD71BAEF04} - Library - Properties - PDFExport - PDFExport - 512 - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - v4.7.2 - - - - - v4.5 - - - true - full - false - ..\GUI\bin\Debug\Plugins\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - x86 - false - - - pdbonly - true - ..\GUI\bin\Release\Plugins\ - TRACE - prompt - 4 - AllRules.ruleset - false - - - - ..\packages\PDFsharp-gdi.1.50.5147\lib\net20\PdfSharp-gdi.dll - - - ..\packages\PDFsharp-gdi.1.50.5147\lib\net20\PdfSharp.Charting-gdi.dll - - - - 3.5 - - - - - - - - - - Form - - - PDFExportFinished.cs - - - Form - - - PDFExportOptions.cs - - - - Form - - - PDFExportProgress.cs - - - - - True - True - Resources.resx - - - True - True - Settings.settings - - - - True - True - Strings.resx - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - False - - - {F52A7548-E60A-485B-9C84-1D2871416DB7} - DiagramEditor - False - - - {A2918CE1-6894-4FC9-A864-4651769D0274} - GUI - False - - - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - - Designer - - - PDFExportFinished.cs - Designer - - - PDFExportOptions.cs - Designer - - - PDFExportProgress.cs - Designer - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.Designer.cs - Designer - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - + + + net5.0-windows7.0 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/src/PDFExport/PDFExportProgress.cs b/src/PDFExport/PDFExportProgress.cs index 134bba59..5c5e786e 100644 --- a/src/PDFExport/PDFExportProgress.cs +++ b/src/PDFExport/PDFExportProgress.cs @@ -6,164 +6,168 @@ namespace PDFExport { - /// - /// A "please wait" form for the PDFExport. - /// - public partial class PDFExportProgress : Form - { - // ======================================================================== - // Attributes - - #region === Attributes - /// - /// The x-location where the entity starts to move. + /// A "please wait" form for the PDFExport. /// - private const int ENTITY_START = 42; + public partial class PDFExportProgress : Form + { + // ======================================================================== + // Attributes - /// - /// The x-location where the entity end its move. - /// - private const int ENTITY_END = 232; + #region === Attributes - /// - /// The current index of the entity which is displayed. - /// - private int currentImageIndex; + /// + /// The x-location where the entity starts to move. + /// + private const int ENTITY_START = 42; - /// - /// A thread which shows the form itself. - /// - private static Thread showThread; + /// + /// The x-location where the entity end its move. + /// + private const int ENTITY_END = 232; - #endregion + /// + /// The current index of the entity which is displayed. + /// + private int currentImageIndex; - // ======================================================================== - // Con- / Destruction + /// + /// A thread which shows the form itself. + /// + private static Thread showThread; - #region === Con- / Destruction + private static bool _stopRequested; - /// - /// Initializes a new PDFExportProgress form. - /// - private PDFExportProgress() - { - InitializeComponent(); - LocalizeComponents(); - } + #endregion - #endregion + // ======================================================================== + // Con- / Destruction - // ======================================================================== - // Methods + #region === Con- / Destruction - #region === Methods + /// + /// Initializes a new PDFExportProgress form. + /// + private PDFExportProgress() + { + InitializeComponent(); - /// - /// Displays the text for the current culture. - /// - private void LocalizeComponents() - { - Text = Strings.Progress_Title; - lblProgress.Text = Strings.Progress_Text; - } + LocalizeComponents(); + } - /// - /// Shows the form asynchonously. To close it, call . - /// - /// The parent form. - public static void ShowAsync(Form parent) - { - showThread = new Thread(Run) - { - IsBackground = true, - Name = "PDFExporterProgressDialogShowThread" - }; + #endregion - Point point = parent == null - ? new Point(Screen.PrimaryScreen.WorkingArea.Width/2, Screen.PrimaryScreen.WorkingArea.Height/2) - : new Point(parent.Left + parent.Width/2, parent.Top + parent.Height/2); + // ======================================================================== + // Methods - showThread.Start(point); - } + #region === Methods - /// - /// Closes an opened PDFExporterProgress form if it is opened. - /// - public static void CloseAsync() - { - if(showThread != null && showThread.IsAlive) - { - showThread.Abort(); - showThread.Join(); - } - } + /// + /// Displays the text for the current culture. + /// + private void LocalizeComponents() + { + Text = Strings.Progress_Title; + lblProgress.Text = Strings.Progress_Text; + } - /// - /// Run-method of the display thread. Shows the form and waits - /// for the end. - /// - /// The center of the form. - private static void Run(Object center) - { - Point point = (Point)center; - PDFExportProgress progressForm = new PDFExportProgress(); - progressForm.Location = new Point(point.X - progressForm.Width/2, point.Y - progressForm.Height/2); - progressForm.Show(); - - try - { - while(true) + /// + /// Shows the form asynchonously. To close it, call . + /// + /// The parent form. + public static void ShowAsync(Form parent) { - Thread.Sleep(5); - Application.DoEvents(); + showThread = new Thread(Run) + { + IsBackground = true, + Name = "PDFExporterProgressDialogShowThread" + }; + + Point point = parent == null + ? new Point(Screen.PrimaryScreen.WorkingArea.Width / 2, Screen.PrimaryScreen.WorkingArea.Height / 2) + : new Point(parent.Left + parent.Width / 2, parent.Top + parent.Height / 2); + + showThread.Start(point); } - } - catch(ThreadAbortException) - { - //We are asked to close the window - progressForm.Close(); - progressForm.Dispose(); - } - } - #endregion + /// + /// Closes an opened PDFExporterProgress form if it is opened. + /// + public static void CloseAsync() + { + _stopRequested = true; + if (showThread != null && showThread.IsAlive) + { + showThread.Join(); + } + } - // ======================================================================== - // Event Handler + /// + /// Run-method of the display thread. Shows the form and waits + /// for the end. + /// + /// The center of the form. + private static void Run(Object center) + { + _stopRequested = false; + Point point = (Point)center; + PDFExportProgress progressForm = new PDFExportProgress(); + progressForm.Location = new Point(point.X - progressForm.Width / 2, point.Y - progressForm.Height / 2); + progressForm.Show(); + + try + { + while (!_stopRequested) + { + Thread.Sleep(5); + Application.DoEvents(); + } + } + catch (ThreadAbortException) + { + //We are asked to close the window + progressForm.Close(); + progressForm.Dispose(); + } + } - #region === Event Handler + #endregion - /// - /// Called when the timer ticks. So this method is called periodically - /// and updates the animation. - /// - /// The caller. - /// Additional information. - private void timer_Tick(object sender, EventArgs e) - { - if(pictureBoxEntity.Left == ENTITY_END) - { - //Next run - pictureBoxEntity.Left = ENTITY_START; - currentImageIndex = (currentImageIndex + 1)%imageListEntities.Images.Count; - pictureBoxEntity.Image = imageListEntities.Images[currentImageIndex]; - } - pictureBoxEntity.Left += 2; - } + // ======================================================================== + // Event Handler - /// - /// Called when the form loads. Initializes it. - /// - /// The caller. - /// Additional information. - private void PDFExportProgress_Load(object sender, EventArgs e) - { - timer.Enabled = true; - pictureBoxEntity.Image = imageListEntities.Images[currentImageIndex]; - } + #region === Event Handler - #endregion - } + /// + /// Called when the timer ticks. So this method is called periodically + /// and updates the animation. + /// + /// The caller. + /// Additional information. + private void timer_Tick(object sender, EventArgs e) + { + if (pictureBoxEntity.Left == ENTITY_END) + { + //Next run + pictureBoxEntity.Left = ENTITY_START; + currentImageIndex = (currentImageIndex + 1) % imageListEntities.Images.Count; + pictureBoxEntity.Image = imageListEntities.Images[currentImageIndex]; + } + pictureBoxEntity.Left += 2; + } + + /// + /// Called when the form loads. Initializes it. + /// + /// The caller. + /// Additional information. + private void PDFExportProgress_Load(object sender, EventArgs e) + { + timer.Enabled = true; + pictureBoxEntity.Image = imageListEntities.Images[currentImageIndex]; + } + + #endregion + } } \ No newline at end of file diff --git a/src/PDFExport/PDFExporter.cs b/src/PDFExport/PDFExporter.cs index fda5203e..c7578bc6 100644 --- a/src/PDFExport/PDFExporter.cs +++ b/src/PDFExport/PDFExporter.cs @@ -7,6 +7,7 @@ using PdfSharp.Pdf; using PDFExport.Lang; using PdfSharp.Fonts; +using System.Text; namespace PDFExport { @@ -53,6 +54,7 @@ public class PDFExporter /// The size of the border arround the diagram. public PDFExporter(string fileName, IDocument nclassDocument, bool selectedOnly, Padding padding) { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); this.fileName = fileName; this.nclassDocument = nclassDocument; this.selectedOnly = selectedOnly; diff --git a/src/PDFExport/PDFGraphics.cs b/src/PDFExport/PDFGraphics.cs index e524621a..1946fe59 100644 --- a/src/PDFExport/PDFGraphics.cs +++ b/src/PDFExport/PDFGraphics.cs @@ -517,7 +517,7 @@ private static XBrush BrushToXBrush(Brush brush) LinearGradientBrush lgBrush; if ((solidBrush = brush as SolidBrush) != null) { - xbrush = new XSolidBrush(solidBrush.Color); + xbrush = new XSolidBrush(XColor.FromArgb(solidBrush.Color)); } else if ((lgBrush = brush as LinearGradientBrush) != null) { @@ -540,7 +540,7 @@ private static XBrush BrushToXBrush(Brush brush) //outside the rectangle. To determine this gap we have to use some trigonometry. //This will happily never the case in NClass. So we don't have to do this here. - xbrush = new XLinearGradientBrush(p1, p2, lgBrush.LinearColors[0], lgBrush.LinearColors[1]); + xbrush = new XLinearGradientBrush(Point.Round(p1), Point.Round(p2), XColor.FromArgb(lgBrush.LinearColors[0]), XColor.FromArgb(lgBrush.LinearColors[1])); } else { @@ -630,7 +630,7 @@ private static XFillMode FillModeToXFillMode(FillMode fillMode) /// The converted PDF-XPen. private static XPen PenToXPen(Pen pen) { - XPen xPen = new XPen(pen.Color, pen.Width) + XPen xPen = new XPen(XColor.FromArgb(pen.Color), pen.Width) { DashOffset = pen.DashOffset, DashStyle = DashStyleToXDashStyle(pen.DashStyle), diff --git a/src/PDFExport/Properties/AssemblyInfo.cs b/src/PDFExport/Properties/AssemblyInfo.cs deleted file mode 100644 index ecf3be75..00000000 --- a/src/PDFExport/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("PDFExport")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Endless Entertainment")] -[assembly: AssemblyProduct("PDFExport")] -[assembly: AssemblyCopyright("Copyright © Endless Entertainment 2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("528e1a29-2938-4dfb-8d6c-a3c84665995f")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/PDFsharp/LICENSE b/src/PDFsharp/LICENSE new file mode 100644 index 00000000..5ed25141 --- /dev/null +++ b/src/PDFsharp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2005-2018 empira Software GmbH, Troisdorf (Germany) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/PDFsharp/README.md b/src/PDFsharp/README.md new file mode 100644 index 00000000..96815532 --- /dev/null +++ b/src/PDFsharp/README.md @@ -0,0 +1,35 @@ +# PDFsharp +A .NET library for processing PDF + +# Resources + +The official project web site: +http://pdfsharp.net/ + +The official peer-to-peer support forum: +http://forum.pdfsharp.net/ + +# Release Notes for PDFsharp/MigraDoc 1.50 (stable) + +The stable version of PDFsharp 1.32 was published in 2013. +So a new stable version is long overdue. + +I really hope the stable version does not have any regressions versus 1.50 beta 3b or later. + +And I hope there are no regressions versus version 1.32 stable. But several bugs have been fixed. +There are a few breaking changes that require code updates. + +To use PDFsharp with Medium Trust you have to get the source code and make some changes. The NuGet packages do not support Medium Trust. +Azure servers do not require Medium Trust. + +I'm afraid that many users who never tried any beta version of PDFsharp 1.50 will now switch from version 1.32 stable to version 1.50 stable. +Nothing wrong about that. I hope we don't get an avalanche of bug reports now. + + +# Which Version to Get? + +The naming convention for the packages has changed. + +If you are using "PdfSharp -Version 1.32.3057" then the version you need now is "PDFsharp-gdi -Version 1.50". +Or get the corresponding package " PDFsharp-MigraDoc-GDI -Version 1.50". + diff --git a/src/PDFsharp/src/BuildAll-PdfSharp.sln b/src/PDFsharp/src/BuildAll-PdfSharp.sln new file mode 100644 index 00000000..51818cd7 --- /dev/null +++ b/src/PDFsharp/src/BuildAll-PdfSharp.sln @@ -0,0 +1,133 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.40629.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PDFsharp", "PdfSharp\PDFsharp.csproj", "{5A6055BC-BF86-4FDD-9F62-0109DB7A303B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp.Charting", "PdfSharp.Charting\PdfSharp.Charting.csproj", "{6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp-gdi", "PdfSharp-gdi\PdfSharp-gdi.csproj", "{5384CE57-1F94-4D22-860D-2E9C1AC12DDF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp.Charting-gdi", "PdfSharp.Charting-gdi\PdfSharp.Charting-gdi.csproj", "{CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp-wpf", "PdfSharp-wpf\PdfSharp-wpf.csproj", "{02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PdfSharp.Charting-wpf", "PdfSharp.Charting-wpf\PdfSharp.Charting-wpf.csproj", "{E6A2734E-0CD6-4210-8AEC-47EE348F8D78}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|Win32.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|x64.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|Any CPU.Build.0 = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|ARM.ActiveCfg = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|Win32.ActiveCfg = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|x64.ActiveCfg = Release|Any CPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B}.Release|x86.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|Win32.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|x64.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Debug|x86.ActiveCfg = Debug|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|Any CPU.Build.0 = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|ARM.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|Win32.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|x64.ActiveCfg = Release|Any CPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF}.Release|x86.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|ARM.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|Win32.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|x64.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Debug|x86.ActiveCfg = Debug|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|Any CPU.Build.0 = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|ARM.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|Win32.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|x64.ActiveCfg = Release|Any CPU + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF}.Release|x86.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|Win32.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|Any CPU.Build.0 = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|ARM.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|Win32.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|x64.ActiveCfg = Release|Any CPU + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA}.Release|x86.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|Win32.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|x64.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Debug|x86.ActiveCfg = Debug|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|Any CPU.Build.0 = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|ARM.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|Win32.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|x64.ActiveCfg = Release|Any CPU + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D}.Release|x86.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|Win32.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Any CPU.Build.0 = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|ARM.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|Win32.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x64.ActiveCfg = Release|Any CPU + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.50.csproj b/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.50.csproj new file mode 100644 index 00000000..1c899976 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.50.csproj @@ -0,0 +1,1121 @@ + + + net5.0-windows7.0 + true + GDI + + + + + + !internal\Directives.cs + + + !internal\TargetContext.cs + + + !JetBrains\Annotations.cs + + + Drawing.BarCodes\BarCode.cs + + + Drawing.BarCodes\BarCodeRenderInfo.cs + + + Drawing.BarCodes\BcgSR.cs + + + Drawing.BarCodes\Code2of5Interleaved.cs + + + Drawing.BarCodes\Code3of9Standard.cs + + + Drawing.BarCodes\CodeBase.cs + + + Drawing.BarCodes\CodeDataMatrix.cs + + + Drawing.BarCodes\CodeOmr.cs + + + Drawing.BarCodes\DataMatrixImage.cs + + + Drawing.BarCodes\enums\AnchorType.cs + + + Drawing.BarCodes\enums\CodeDirection.cs + + + Drawing.BarCodes\enums\CodeType.cs + + + Drawing.BarCodes\enums\DataMatrixEncoding.cs + + + Drawing.BarCodes\enums\MarkDistance.cs + + + Drawing.BarCodes\enums\TextLocation.cs + + + Drawing.BarCodes\MatrixCode.cs + + + Drawing.BarCodes\OmrData.cs + + + Drawing.BarCodes\ThickThinBarcodeRenderer.cs + + + Drawing.Internal\IImageImporter.cs + + + Drawing.Internal\ImageImporter.cs + + + Drawing.Internal\ImageImporterBmp.cs + + + Drawing.Internal\ImageImporterJpeg.cs + + + Drawing.Internal\ImageImporterRoot.cs + + + Drawing.Layout\enums\XParagraphAlignment.cs + + + Drawing.Layout\XTextFormatter.cs + + + Drawing.Pdf\enums\DirtyFlags.cs + + + Drawing.Pdf\enums\StreamMode.cs + + + Drawing.Pdf\PdfGraphicsState.cs + + + Drawing.Pdf\XGraphicsPdfRenderer.cs + + + Drawing\CoreGraphicsPath.cs + + + Drawing\enums\PathStart.cs + + + Drawing\enums\XColorSpace.cs + + + Drawing\enums\XCombineMode.cs + + + Drawing\enums\XDashStyle.cs + + + Drawing\enums\XFillMode.cs + + + Drawing\enums\XFontStyle.cs + + + Drawing\enums\XGdiFontStyle.cs + + + Drawing\enums\XGraphicRenderTarget.cs + + + Drawing\enums\XGraphicsPathItemType.cs + + + Drawing\enums\XGraphicsPdfPageOptions.cs + + + Drawing\enums\XGraphicsUnit.cs + + + Drawing\enums\XKnownColor.cs + + + Drawing\enums\XLineAlignment.cs + + + Drawing\enums\XLinearGradientMode.cs + + + Drawing\enums\XLineCap.cs + + + Drawing\enums\XLineJoin.cs + + + Drawing\enums\XMatrixOrder.cs + + + Drawing\enums\XPageDirection.cs + + + Drawing\enums\XSmoothingMode.cs + + + Drawing\enums\XStringAlignment.cs + + + Drawing\enums\XStyleSimulations.cs + + + Drawing\enums\XSweepDirection.cs + + + Drawing\FontFamilyCache.cs + + + Drawing\FontFamilyInternal.cs + + + Drawing\FontHelper.cs + + + Drawing\GeometryHelper.cs + + + Drawing\GraphicsStateStack.cs + + + Drawing\ImageHelper.cs + + + Drawing\InternalGraphicsState.cs + + + Drawing\IXGraphicsRenderer.cs + + + Drawing\PdfFontOptions.cs + + + Drawing\XBitmapDecoder.cs + + + Drawing\XBitmapEncoder.cs + + + Drawing\XBitmapImage.cs + + + Drawing\XBitmapSource.cs + + + Drawing\XBrush.cs + + + Drawing\XBrushes.cs + + + Drawing\XColor.cs + + + Drawing\XColorResourceManager.cs + + + Drawing\XColors.cs + + + Drawing\XConvert.cs + + + Drawing\XFont.cs + + + Drawing\XFontFamily.cs + + + Drawing\XFontMetrics.cs + + + Drawing\XFontSource.cs + + + Drawing\XFontStretch.cs + + + Drawing\XFontWeight.cs + + + Drawing\XFontWeights.cs + + + Drawing\XForm.cs + + + Drawing\XGlyphTypeface.cs + + + Drawing\XGradientBrush.cs + + + Drawing\XGraphics.cs + + + Drawing\XGraphicsContainer.cs + + + Drawing\XGraphicsPath.cs + + + Drawing\XGraphicsPathInternals.cs + + + Drawing\XGraphicsPathItem.cs + + + Drawing\XGraphicsState.cs + + + Drawing\XImage.cs + + + Drawing\XImageFormat.cs + + + Drawing\XKnownColorTable.cs + + + Drawing\XLinearGradientBrush.cs + + + Drawing\XMatrix.cs + + + Drawing\XPdfForm.cs + + + Drawing\XPen.cs + + + Drawing\XPens.cs + + + Drawing\XPoint.cs + + + Drawing\XPrivateFontCollection.cs + + + Drawing\XRadialGradientBrush.cs + + + Drawing\XRect.cs + + + Drawing\XSize.cs + + + Drawing\XSolidBrush.cs + + + Drawing\XStringFormat.cs + + + Drawing\XStringFormats.cs + + + Drawing\XTypeface.cs + + + Drawing\XUnit.cs + + + Drawing\XVector.cs + + + Events\DocumentEvents.cs + + + Fonts.OpenType\enums\FontTechnology.cs + + + Fonts.OpenType\enums\TableTagNames.cs + + + Fonts.OpenType\FontDescriptor.cs + + + Fonts.OpenType\GenericFontTable.cs + + + Fonts.OpenType\GlyphDataTable.cs + + + Fonts.OpenType\GlyphTypefaceCache.cs + + + Fonts.OpenType\IndexToLocationTable.cs + + + Fonts.OpenType\IRefFontTable.cs + + + Fonts.OpenType\OpenTypeDescriptor.cs + + + Fonts.OpenType\OpenTypeFontface.cs + + + Fonts.OpenType\OpenTypeFontfaceCache.cs + + + Fonts.OpenType\OpenTypeFontTable.cs + + + Fonts.OpenType\OpenTypeFontTables.cs + + + Fonts.OpenType\OpenTypeFontWriter.cs + + + Fonts.OpenType\TableDirectoryEntry.cs + + + Fonts\AdobeGlyphList20.cs + + + Fonts\AdobeGlyphListForNewFonts.cs + + + Fonts\CMapInfo.cs + + + Fonts\FontDescriptorCache.cs + + + Fonts\FontFactory.cs + + + Fonts\FontResolverInfo.cs + + + Fonts\FontResolvingOptions.cs + + + Fonts\FontWriter.cs + + + Fonts\GlobalFontSettings.cs + + + Fonts\IFontResolver.cs + + + Fonts\PlatformFontResolver.cs + + + Fonts\PlatformFontResolverInfo.cs + + + Forms\ColorComboBox.cs + Component + + + Forms\DeviceInfos.cs + + + Forms\enums\RenderMode.cs + + + Forms\enums\Zoom.cs + + + Forms\PagePreview.cs + + + Forms\PagePreviewCanvas.cs + + + Internal\Calc.cs + + + Internal\ColorHelper.cs + + + Internal\Diagnostics.cs + + + Internal\DiagnosticsHelper.cs + + + Internal\DoubleUtil.cs + + + Internal\Lock.cs + + + Internal\NativeMethods.cs + + + Internal\TokenizerHelper.cs + + + Pdf.AcroForms\enums\PdfAcroFieldFlags.cs + + + Pdf.AcroForms\PdfAcroField.cs + + + Pdf.AcroForms\PdfAcroForm.cs + + + Pdf.AcroForms\PdfButtonField.cs + + + Pdf.AcroForms\PdfCheckBoxField.cs + + + Pdf.AcroForms\PdfChoiceField.cs + + + Pdf.AcroForms\PdfComboBoxField.cs + + + Pdf.AcroForms\PdfGenericField.cs + + + Pdf.AcroForms\PdfListBoxField.cs + + + Pdf.AcroForms\PdfPushButtonField.cs + + + Pdf.AcroForms\PdfRadioButtonField.cs + + + Pdf.AcroForms\PdfSignatureField.cs + + + Pdf.AcroForms\PdfTextField.cs + + + Pdf.Actions\enums\PdfNamedActionNames.cs + + + Pdf.Actions\PdfAction.cs + + + Pdf.Actions\PdfEmbeddedGoToAction.cs + + + Pdf.Actions\PdfRemoteGoToAction.cs + + + Pdf.Actions\PdfGoToAction.cs + + + Pdf.Advanced\IContentStream.cs + + + Pdf.Advanced\PdfCatalog.cs + + + Pdf.Advanced\PdfCIDFont.cs + + + Pdf.Advanced\PdfContent.cs + + + Pdf.Advanced\PdfContents.cs + + + Pdf.Advanced\PdfCrossReferenceStream.cs + + + Pdf.Advanced\PdfCrossReferenceTable.cs + + + Pdf.Advanced\PdfDictionaryWithContentStream.cs + + + Pdf.Advanced\PdfEmbeddedFileStream.cs + + + Pdf.Advanced\PdfExtGState.cs + + + Pdf.Advanced\PdfExtGStateTable.cs + + + Pdf.Advanced\PdfFileSpecification.cs + + + Pdf.Advanced\PdfFont.cs + + + Pdf.Advanced\PdfFontDescriptor.cs + + + Pdf.Advanced\PdfFontTable.cs + + + Pdf.Advanced\PdfFormXObject.cs + + + Pdf.Advanced\PdfFormXObjectTable.cs + + + Pdf.Advanced\PdfGroupAttributes.cs + + + Pdf.Advanced\PdfImage.cs + + + Pdf.Advanced\PdfImage.FaxEncode.cs + + + Pdf.Advanced\PdfImageTable.cs + + + Pdf.Advanced\PdfImportedObjectTable.cs + + + Pdf.Advanced\PdfInternals.cs + + + Pdf.Advanced\PdfNamedDestinationParameters.cs + + + Pdf.Advanced\PdfNameDictionary.cs + + + Pdf.Advanced\PdfObjectInternals.cs + + + Pdf.Advanced\PdfObjectStream.cs + + + Pdf.Advanced\PdfPageInheritableObjects.cs + + + Pdf.Advanced\PdfPageInterals.cs + + + Pdf.Advanced\PdfReference.cs + + + Pdf.Advanced\PdfResourceMap.cs + + + Pdf.Advanced\PdfResources.cs + + + Pdf.Advanced\PdfResourceTable.cs + + + Pdf.Advanced\PdfShading.cs + + + Pdf.Advanced\PdfShadingPattern.cs + + + Pdf.Advanced\PdfSoftMask.cs + + + Pdf.Advanced\PdfTilingPattern.cs + + + Pdf.Advanced\PdfToUnicodeMap.cs + + + Pdf.Advanced\PdfTrailer.cs + + + Pdf.Advanced\PdfTransparencyGroupAttributes.cs + + + Pdf.Advanced\PdfTrueTypeFont.cs + + + Pdf.Advanced\PdfType0Font.cs + + + Pdf.Advanced\PdfType1Font.cs + + + Pdf.Advanced\PdfXObject.cs + + + Pdf.Annotations\enums\PdfAnnotationFlags.cs + + + Pdf.Annotations\enums\PdfRubberStampAnnotationIcon.cs + + + Pdf.Annotations\enums\PdfTextAnnotationIcon.cs + + + Pdf.Annotations\PdfAnnotation.cs + + + Pdf.Annotations\PdfAnnotations.cs + + + Pdf.Annotations\PdfGenericAnnotation.cs + + + Pdf.Annotations\PdfLinkAnnotation.cs + + + Pdf.Annotations\PdfRubberStampAnnotation.cs + + + Pdf.Annotations\PdfTextAnnotation.cs + + + Pdf.Annotations\PdfWidgetAnnotation.cs + + + Pdf.Content.Objects\CObjects.cs + + + Pdf.Content.Objects\enum\OpCodeFlags.cs + + + Pdf.Content.Objects\enum\OpCodeName.cs + + + Pdf.Content.Objects\Operators.cs + + + Pdf.Content\Chars.cs + + + Pdf.Content\CLexer.cs + + + Pdf.Content\ContentReader.cs + + + Pdf.Content\ContentReaderException.cs + + + Pdf.Content\ContentWriter.cs + + + Pdf.Content\CParser.cs + + + Pdf.Content\enums\Symbol.cs + + + Pdf.Filters\Ascii85Decode.cs + + + Pdf.Filters\AsciiHexDecode.cs + + + Pdf.Filters\Filter.cs + + + Pdf.Filters\Filtering.cs + + + Pdf.Filters\FlateDecode.cs + + + Pdf.Filters\LzwDecode.cs + + + Pdf.Internal\AnsiEncoding.cs + + + Pdf.Internal\ColorSpaceHelper.cs + + + Pdf.Internal\DocEncoding.cs + + + Pdf.Internal\GlobalObjectTable.cs + + + Pdf.Internal\PdfDiagnostics.cs + + + Pdf.Internal\PdfEncoders.cs + + + Pdf.Internal\RawEncoding.cs + + + Pdf.Internal\RawUnicodeEncoding.cs + + + Pdf.Internal\ThreadLocalStorage.cs + + + Pdf.IO\Chars.cs + + + Pdf.IO\enums\PasswordValidity.cs + + + Pdf.IO\enums\PdfDocumentOpenMode.cs + + + Pdf.IO\enums\PdfWriterLayout.cs + + + Pdf.IO\enums\PdfWriterOptions.cs + + + Pdf.IO\enums\Symbol.cs + + + Pdf.IO\Lexer.cs + + + Pdf.IO\Parser.cs + + + Pdf.IO\PdfReader.cs + + + Pdf.IO\PdfReaderException.cs + + + Pdf.IO\PdfWriter.cs + + + Pdf.IO\ShiftStack.cs + + + Pdf.Printing\PdfFilePrinter.cs + + + Pdf.Security\enums\PdfDocumentSecurity.cs + + + Pdf.Security\enums\PdfUserAccessPermission.cs + + + Pdf.Security\MD5Managed.cs + + + Pdf.Security\PdfSecurityHandler.cs + + + Pdf.Security\PdfSecuritySettings.cs + + + Pdf.Security\PdfStandardSecurityHandler.cs + + + Pdf.Structure\PdfAttributesBase.cs + + + Pdf.Structure\PdfLayoutAttributes.cs + + + Pdf.Structure\PdfMarkedContentReference.cs + + + Pdf.Structure\PdfMarkInformation.cs + + + Pdf.Structure\PdfObjectReference.cs + + + Pdf.Structure\PdfStructureElement.cs + + + Pdf.Structure\PdfStructureTreeRoot.cs + + + Pdf.Structure\PdfTableAttributes.cs + + + Pdf\EntryInfoAttribute.cs + + + Pdf\enums\DocumentState.cs + + + Pdf\enums\PdfColorMode.cs + + + Pdf\enums\PdfCustomValueCompression.cs + + + Pdf\enums\PdfFlateEncodeMode.cs + + + Pdf\enums\PdfFontEmbedding.cs + + + Pdf\enums\PdfFontEncoding.cs + + + Pdf\enums\PdfOutlineStyle.cs + + + Pdf\enums\PdfPageDestinationType.cs + + + Pdf\enums\PdfPageLayout.cs + + + Pdf\enums\PdfPageMode.cs + + + Pdf\enums\PdfReadingDirection.cs + + + Pdf\enums\PdfTextStringEncoding.cs + + + Pdf\enums\PdfUseFlateDecoderForJpegImages.cs + + + Pdf\KeysBase.cs + + + Pdf\KeysMeta.cs + + + Pdf\PdfArray.cs + + + Pdf\PdfBoolean.cs + + + Pdf\PdfBooleanObject.cs + + + Pdf\PdfCustomValue.cs + + + Pdf\PdfCustomValues.cs + + + Pdf\PdfNameTreeNode.cs + + + Pdf\PdfDate.cs + + + Pdf\PdfDictionary.cs + + + Pdf\PdfDocument.cs + + + Pdf\PdfDocumentInformation.cs + + + Pdf\PdfDocumentOptions.cs + + + Pdf\PdfDocumentSettings.cs + + + Pdf\PdfInteger.cs + + + Pdf\PdfIntegerObject.cs + + + Pdf\PdfItem.cs + + + Pdf\PdfLiteral.cs + + + Pdf\PdfMetadata.cs + + + Pdf\PdfName.cs + + + Pdf\PdfNameObject.cs + + + Pdf\PdfNull.cs + + + Pdf\PdfNullObject.cs + + + Pdf\PdfNumber.cs + + + Pdf\PdfNumberObject.cs + + + Pdf\PdfNumberTreeNode.cs + + + Pdf\PdfObject.cs + + + Pdf\PdfObjectID.cs + + + Pdf\PdfOutline.cs + + + Pdf\PdfOutlineCollection.cs + + + Pdf\PdfPage.cs + + + Pdf\PdfPages.cs + + + Pdf\PdfReal.cs + + + Pdf\PdfRealObject.cs + + + Pdf\PdfRectangle.cs + + + Pdf\PdfReferenceTable.cs + + + Pdf\PdfString.cs + + + Pdf\PdfStringObject.cs + + + Pdf\PdfUInteger.cs + + + Pdf\PdfUIntegerObject.cs + + + Pdf\PdfViewerPreferences.cs + + + Pdf\TrimMargins.cs + + + root\enums\PageOrientation.cs + + + root\enums\PageSize.cs + + + root\enums\PSMsgID.cs + + + root\PageSizeConverter.cs + + + root\PdfSharpException.cs + + + root\ProductVersionInfo.cs + + + root\PSSR.cs + + + root\VersionInfo.cs + + + SharpZipLib\Checksums\Adler32.cs + + + SharpZipLib\Checksums\CRC32.cs + + + SharpZipLib\Checksums\IChecksum.cs + + + SharpZipLib\SharpZipBaseException.cs + + + SharpZipLib\Zip\Compression\Deflater.cs + + + SharpZipLib\Zip\Compression\DeflaterConstants.cs + + + SharpZipLib\Zip\Compression\DeflaterEngine.cs + + + SharpZipLib\Zip\Compression\DeflaterHuffman.cs + + + SharpZipLib\Zip\Compression\DeflaterPending.cs + + + SharpZipLib\Zip\Compression\Inflater.cs + + + SharpZipLib\Zip\Compression\InflaterDynHeader.cs + + + SharpZipLib\Zip\Compression\InflaterHuffmanTree.cs + + + SharpZipLib\Zip\Compression\PendingBuffer.cs + + + SharpZipLib\Zip\Compression\Streams\DeflaterOutputStream.cs + + + SharpZipLib\Zip\Compression\Streams\InflaterInputStream.cs + + + SharpZipLib\Zip\Compression\Streams\OutputWindow.cs + + + SharpZipLib\Zip\Compression\Streams\StreamManipulator.cs + + + SharpZipLib\Zip\ZipConstants.cs + + + SharpZipLib\Zip\ZipException.cs + + + Forms\PagePreview.resx + PagePreview.cs + + + Forms\PagePreviewCanvas.resx + PagePreviewCanvas.cs + + + + + Resources\Messages.de.restext + + + Resources\Messages.restext + + + + + + + + + + SharpZipLib\ReadMe.txt + + + + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.csproj b/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.csproj new file mode 100644 index 00000000..941eee61 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-gdi/PdfSharp-gdi.csproj @@ -0,0 +1,1260 @@ + + + + Local + 9.0.30729 + 2.0 + {5384CE57-1F94-4D22-860D-2E9C1AC12DDF} + Debug + AnyCPU + + + + + PdfSharp-gdi + StrongnameKey.snk + JScript + Grid + IE50 + false + Library + PdfSharp + OnBuildSuccess + + + + + + + true + 3.5 + false + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + SAK + SAK + SAK + SAK + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;GDI;UseGdiObjects + + + true + 4096 + false + + + false + false + false + false + 4 + default + prompt + AnyCPU + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + TRACE;GDI;UseGdiObjects + bin\Release\PdfSharp-gdi.xml + false + 4096 + false + + + true + false + false + true + 4 + AnyCPU + none + AllRules.ruleset + + + + System + + + System.Drawing + + + System.Windows.Forms + + + + + !internal\Configuration.cs + + + !internal\Directives.cs + + + !internal\TargetContext.cs + + + !JetBrains\Annotations.cs + + + Drawing.BarCodes\BarCode.cs + + + Drawing.BarCodes\BarCodeRenderInfo.cs + + + Drawing.BarCodes\BcgSR.cs + + + Drawing.BarCodes\Code2of5Interleaved.cs + + + Drawing.BarCodes\Code3of9Standard.cs + + + Drawing.BarCodes\CodeBase.cs + + + Drawing.BarCodes\CodeDataMatrix.cs + + + Drawing.BarCodes\CodeOmr.cs + + + Drawing.BarCodes\DataMatrixImage.cs + + + Drawing.BarCodes\enums\AnchorType.cs + + + Drawing.BarCodes\enums\CodeDirection.cs + + + Drawing.BarCodes\enums\CodeType.cs + + + Drawing.BarCodes\enums\DataMatrixEncoding.cs + + + Drawing.BarCodes\enums\MarkDistance.cs + + + Drawing.BarCodes\enums\TextLocation.cs + + + Drawing.BarCodes\MatrixCode.cs + + + Drawing.BarCodes\OmrData.cs + + + Drawing.BarCodes\ThickThinBarcodeRenderer.cs + + + Drawing.Internal\IImageImporter.cs + + + Drawing.Internal\ImageImporter.cs + + + Drawing.Internal\ImageImporterBmp.cs + + + Drawing.Internal\ImageImporterJpeg.cs + + + Drawing.Internal\ImageImporterRoot.cs + + + Drawing.Layout\enums\XParagraphAlignment.cs + + + Drawing.Layout\XTextFormatter.cs + + + Drawing.Pdf\enums\DirtyFlags.cs + + + Drawing.Pdf\enums\StreamMode.cs + + + Drawing.Pdf\PdfGraphicsState.cs + + + Drawing.Pdf\XGraphicsPdfRenderer.cs + + + Drawing\CoreGraphicsPath.cs + + + Drawing\enums\PathStart.cs + + + Drawing\enums\XColorSpace.cs + + + Drawing\enums\XCombineMode.cs + + + Drawing\enums\XDashStyle.cs + + + Drawing\enums\XFillMode.cs + + + Drawing\enums\XFontStyle.cs + + + Drawing\enums\XGdiFontStyle.cs + + + Drawing\enums\XGraphicRenderTarget.cs + + + Drawing\enums\XGraphicsPathItemType.cs + + + Drawing\enums\XGraphicsPdfPageOptions.cs + + + Drawing\enums\XGraphicsUnit.cs + + + Drawing\enums\XKnownColor.cs + + + Drawing\enums\XLineAlignment.cs + + + Drawing\enums\XLinearGradientMode.cs + + + Drawing\enums\XLineCap.cs + + + Drawing\enums\XLineJoin.cs + + + Drawing\enums\XMatrixOrder.cs + + + Drawing\enums\XPageDirection.cs + + + Drawing\enums\XSmoothingMode.cs + + + Drawing\enums\XStringAlignment.cs + + + Drawing\enums\XStyleSimulations.cs + + + Drawing\enums\XSweepDirection.cs + + + Drawing\FontFamilyCache.cs + + + Drawing\FontFamilyInternal.cs + + + Drawing\FontHelper.cs + + + Drawing\GeometryHelper.cs + + + Drawing\GraphicsStateStack.cs + + + Drawing\ImageHelper.cs + + + Drawing\InternalGraphicsState.cs + + + Drawing\IXGraphicsRenderer.cs + + + Drawing\PdfFontOptions.cs + + + Drawing\XBitmapDecoder.cs + + + Drawing\XBitmapEncoder.cs + + + Drawing\XBitmapImage.cs + + + Drawing\XBitmapSource.cs + + + Drawing\XBrush.cs + + + Drawing\XBrushes.cs + + + Drawing\XColor.cs + + + Drawing\XColorResourceManager.cs + + + Drawing\XColors.cs + + + Drawing\XConvert.cs + + + Drawing\XFont.cs + + + Drawing\XFontFamily.cs + + + Drawing\XFontMetrics.cs + + + Drawing\XFontSource.cs + + + Drawing\XFontStretch.cs + + + Drawing\XFontWeight.cs + + + Drawing\XFontWeights.cs + + + Drawing\XForm.cs + + + Drawing\XGlyphTypeface.cs + + + Drawing\XGradientBrush.cs + + + Drawing\XGraphics.cs + + + Drawing\XGraphicsContainer.cs + + + Drawing\XGraphicsPath.cs + + + Drawing\XGraphicsPathInternals.cs + + + Drawing\XGraphicsPathItem.cs + + + Drawing\XGraphicsState.cs + + + Drawing\XImage.cs + + + Drawing\XImageFormat.cs + + + Drawing\XKnownColorTable.cs + + + Drawing\XLinearGradientBrush.cs + + + Drawing\XMatrix.cs + + + Drawing\XPdfForm.cs + + + Drawing\XPen.cs + + + Drawing\XPens.cs + + + Drawing\XPoint.cs + + + Drawing\XPrivateFontCollection.cs + + + Drawing\XRadialGradientBrush.cs + + + Drawing\XRect.cs + + + Drawing\XSize.cs + + + Drawing\XSolidBrush.cs + + + Drawing\XStringFormat.cs + + + Drawing\XStringFormats.cs + + + Drawing\XTypeface.cs + + + Drawing\XUnit.cs + + + Drawing\XVector.cs + + + Events\DocumentEvents.cs + + + Fonts.OpenType\enums\FontTechnology.cs + + + Fonts.OpenType\enums\TableTagNames.cs + + + Fonts.OpenType\FontDescriptor.cs + + + Fonts.OpenType\GenericFontTable.cs + + + Fonts.OpenType\GlyphDataTable.cs + + + Fonts.OpenType\GlyphTypefaceCache.cs + + + Fonts.OpenType\IndexToLocationTable.cs + + + Fonts.OpenType\IRefFontTable.cs + + + Fonts.OpenType\OpenTypeDescriptor.cs + + + Fonts.OpenType\OpenTypeFontface.cs + + + Fonts.OpenType\OpenTypeFontfaceCache.cs + + + Fonts.OpenType\OpenTypeFontTable.cs + + + Fonts.OpenType\OpenTypeFontTables.cs + + + Fonts.OpenType\OpenTypeFontWriter.cs + + + Fonts.OpenType\TableDirectoryEntry.cs + + + Fonts\AdobeGlyphList20.cs + + + Fonts\AdobeGlyphListForNewFonts.cs + + + Fonts\CMapInfo.cs + + + Fonts\FontDescriptorCache.cs + + + Fonts\FontFactory.cs + + + Fonts\FontResolverInfo.cs + + + Fonts\FontResolvingOptions.cs + + + Fonts\FontWriter.cs + + + Fonts\GlobalFontSettings.cs + + + Fonts\IFontResolver.cs + + + Fonts\PlatformFontResolver.cs + + + Fonts\PlatformFontResolverInfo.cs + + + Forms\ColorComboBox.cs + Component + + + Forms\DeviceInfos.cs + + + Forms\enums\RenderMode.cs + + + Forms\enums\Zoom.cs + + + Forms\PagePreview.cs + UserControl + + + Forms\PagePreviewCanvas.cs + Component + + + Internal\Calc.cs + + + Internal\ColorHelper.cs + + + Internal\Diagnostics.cs + + + Internal\DiagnosticsHelper.cs + + + Internal\DoubleUtil.cs + + + Internal\Lock.cs + + + Internal\NativeMethods.cs + + + Internal\TokenizerHelper.cs + + + Pdf.AcroForms\enums\PdfAcroFieldFlags.cs + + + Pdf.AcroForms\PdfAcroField.cs + + + Pdf.AcroForms\PdfAcroForm.cs + + + Pdf.AcroForms\PdfButtonField.cs + + + Pdf.AcroForms\PdfCheckBoxField.cs + + + Pdf.AcroForms\PdfChoiceField.cs + + + Pdf.AcroForms\PdfComboBoxField.cs + + + Pdf.AcroForms\PdfGenericField.cs + + + Pdf.AcroForms\PdfListBoxField.cs + + + Pdf.AcroForms\PdfPushButtonField.cs + + + Pdf.AcroForms\PdfRadioButtonField.cs + + + Pdf.AcroForms\PdfSignatureField.cs + + + Pdf.AcroForms\PdfTextField.cs + + + Pdf.Actions\enums\PdfNamedActionNames.cs + + + Pdf.Actions\PdfAction.cs + + + Pdf.Actions\PdfEmbeddedGoToAction.cs + + + Pdf.Actions\PdfRemoteGoToAction.cs + + + Pdf.Actions\PdfGoToAction.cs + + + Pdf.Advanced\IContentStream.cs + + + Pdf.Advanced\PdfCatalog.cs + + + Pdf.Advanced\PdfCIDFont.cs + + + Pdf.Advanced\PdfContent.cs + + + Pdf.Advanced\PdfContents.cs + + + Pdf.Advanced\PdfCrossReferenceStream.cs + + + Pdf.Advanced\PdfCrossReferenceTable.cs + + + Pdf.Advanced\PdfDictionaryWithContentStream.cs + + + Pdf.Advanced\PdfEmbeddedFileStream.cs + + + Pdf.Advanced\PdfExtGState.cs + + + Pdf.Advanced\PdfExtGStateTable.cs + + + Pdf.Advanced\PdfFileSpecification.cs + + + Pdf.Advanced\PdfFont.cs + + + Pdf.Advanced\PdfFontDescriptor.cs + + + Pdf.Advanced\PdfFontTable.cs + + + Pdf.Advanced\PdfFormXObject.cs + + + Pdf.Advanced\PdfFormXObjectTable.cs + + + Pdf.Advanced\PdfGroupAttributes.cs + + + Pdf.Advanced\PdfImage.cs + + + Pdf.Advanced\PdfImage.FaxEncode.cs + + + Pdf.Advanced\PdfImageTable.cs + + + Pdf.Advanced\PdfImportedObjectTable.cs + + + Pdf.Advanced\PdfInternals.cs + + + Pdf.Advanced\PdfNamedDestinationParameters.cs + + + Pdf.Advanced\PdfNameDictionary.cs + + + Pdf.Advanced\PdfObjectInternals.cs + + + Pdf.Advanced\PdfObjectStream.cs + + + Pdf.Advanced\PdfPageInheritableObjects.cs + + + Pdf.Advanced\PdfPageInterals.cs + + + Pdf.Advanced\PdfReference.cs + + + Pdf.Advanced\PdfResourceMap.cs + + + Pdf.Advanced\PdfResources.cs + + + Pdf.Advanced\PdfResourceTable.cs + + + Pdf.Advanced\PdfShading.cs + + + Pdf.Advanced\PdfShadingPattern.cs + + + Pdf.Advanced\PdfSoftMask.cs + + + Pdf.Advanced\PdfTilingPattern.cs + + + Pdf.Advanced\PdfToUnicodeMap.cs + + + Pdf.Advanced\PdfTrailer.cs + + + Pdf.Advanced\PdfTransparencyGroupAttributes.cs + + + Pdf.Advanced\PdfTrueTypeFont.cs + + + Pdf.Advanced\PdfType0Font.cs + + + Pdf.Advanced\PdfType1Font.cs + + + Pdf.Advanced\PdfXObject.cs + + + Pdf.Annotations\enums\PdfAnnotationFlags.cs + + + Pdf.Annotations\enums\PdfRubberStampAnnotationIcon.cs + + + Pdf.Annotations\enums\PdfTextAnnotationIcon.cs + + + Pdf.Annotations\PdfAnnotation.cs + + + Pdf.Annotations\PdfAnnotations.cs + + + Pdf.Annotations\PdfGenericAnnotation.cs + + + Pdf.Annotations\PdfLinkAnnotation.cs + + + Pdf.Annotations\PdfRubberStampAnnotation.cs + + + Pdf.Annotations\PdfTextAnnotation.cs + + + Pdf.Annotations\PdfWidgetAnnotation.cs + + + Pdf.Content.Objects\CObjects.cs + + + Pdf.Content.Objects\enum\OpCodeFlags.cs + + + Pdf.Content.Objects\enum\OpCodeName.cs + + + Pdf.Content.Objects\Operators.cs + + + Pdf.Content\Chars.cs + + + Pdf.Content\CLexer.cs + + + Pdf.Content\ContentReader.cs + + + Pdf.Content\ContentReaderException.cs + + + Pdf.Content\ContentWriter.cs + + + Pdf.Content\CParser.cs + + + Pdf.Content\enums\Symbol.cs + + + Pdf.Filters\Ascii85Decode.cs + + + Pdf.Filters\AsciiHexDecode.cs + + + Pdf.Filters\Filter.cs + + + Pdf.Filters\Filtering.cs + + + Pdf.Filters\FlateDecode.cs + + + Pdf.Filters\LzwDecode.cs + + + Pdf.Internal\AnsiEncoding.cs + + + Pdf.Internal\ColorSpaceHelper.cs + + + Pdf.Internal\DocEncoding.cs + + + Pdf.Internal\GlobalObjectTable.cs + + + Pdf.Internal\PdfDiagnostics.cs + + + Pdf.Internal\PdfEncoders.cs + + + Pdf.Internal\RawEncoding.cs + + + Pdf.Internal\RawUnicodeEncoding.cs + + + Pdf.Internal\ThreadLocalStorage.cs + + + Pdf.IO\Chars.cs + + + Pdf.IO\enums\PasswordValidity.cs + + + Pdf.IO\enums\PdfDocumentOpenMode.cs + + + Pdf.IO\enums\PdfWriterLayout.cs + + + Pdf.IO\enums\PdfWriterOptions.cs + + + Pdf.IO\enums\Symbol.cs + + + Pdf.IO\Lexer.cs + + + Pdf.IO\Parser.cs + + + Pdf.IO\PdfReader.cs + + + Pdf.IO\PdfReaderException.cs + + + Pdf.IO\PdfWriter.cs + + + Pdf.IO\ShiftStack.cs + + + Pdf.Printing\PdfFilePrinter.cs + + + Pdf.Security\enums\PdfDocumentSecurity.cs + + + Pdf.Security\enums\PdfUserAccessPermission.cs + + + Pdf.Security\MD5Managed.cs + + + Pdf.Security\PdfSecurityHandler.cs + + + Pdf.Security\PdfSecuritySettings.cs + + + Pdf.Security\PdfStandardSecurityHandler.cs + + + Pdf.Structure\PdfAttributesBase.cs + + + Pdf.Structure\PdfLayoutAttributes.cs + + + Pdf.Structure\PdfMarkedContentReference.cs + + + Pdf.Structure\PdfMarkInformation.cs + + + Pdf.Structure\PdfObjectReference.cs + + + Pdf.Structure\PdfStructureElement.cs + + + Pdf.Structure\PdfStructureTreeRoot.cs + + + Pdf.Structure\PdfTableAttributes.cs + + + Pdf\EntryInfoAttribute.cs + + + Pdf\enums\DocumentState.cs + + + Pdf\enums\PdfColorMode.cs + + + Pdf\enums\PdfCustomValueCompression.cs + + + Pdf\enums\PdfFlateEncodeMode.cs + + + Pdf\enums\PdfFontEmbedding.cs + + + Pdf\enums\PdfFontEncoding.cs + + + Pdf\enums\PdfOutlineStyle.cs + + + Pdf\enums\PdfPageDestinationType.cs + + + Pdf\enums\PdfPageLayout.cs + + + Pdf\enums\PdfPageMode.cs + + + Pdf\enums\PdfReadingDirection.cs + + + Pdf\enums\PdfTextStringEncoding.cs + + + Pdf\enums\PdfUseFlateDecoderForJpegImages.cs + + + Pdf\KeysBase.cs + + + Pdf\KeysMeta.cs + + + Pdf\PdfArray.cs + + + Pdf\PdfBoolean.cs + + + Pdf\PdfBooleanObject.cs + + + Pdf\PdfCustomValue.cs + + + Pdf\PdfCustomValues.cs + + + Pdf\PdfNameTreeNode.cs + + + Pdf\PdfDate.cs + + + Pdf\PdfDictionary.cs + + + Pdf\PdfDocument.cs + + + Pdf\PdfDocumentInformation.cs + + + Pdf\PdfDocumentOptions.cs + + + Pdf\PdfDocumentSettings.cs + + + Pdf\PdfInteger.cs + + + Pdf\PdfIntegerObject.cs + + + Pdf\PdfItem.cs + + + Pdf\PdfLiteral.cs + + + Pdf\PdfMetadata.cs + + + Pdf\PdfName.cs + + + Pdf\PdfNameObject.cs + + + Pdf\PdfNull.cs + + + Pdf\PdfNullObject.cs + + + Pdf\PdfNumber.cs + + + Pdf\PdfNumberObject.cs + + + Pdf\PdfNumberTreeNode.cs + + + Pdf\PdfObject.cs + + + Pdf\PdfObjectID.cs + + + Pdf\PdfOutline.cs + + + Pdf\PdfOutlineCollection.cs + + + Pdf\PdfPage.cs + + + Pdf\PdfPages.cs + + + Pdf\PdfReal.cs + + + Pdf\PdfRealObject.cs + + + Pdf\PdfRectangle.cs + + + Pdf\PdfReferenceTable.cs + + + Pdf\PdfString.cs + + + Pdf\PdfStringObject.cs + + + Pdf\PdfUInteger.cs + + + Pdf\PdfUIntegerObject.cs + + + Pdf\PdfViewerPreferences.cs + + + Pdf\TrimMargins.cs + + + Properties\AssemblyInfo.cs + + + root\enums\PageOrientation.cs + + + root\enums\PageSize.cs + + + root\enums\PSMsgID.cs + + + root\PageSizeConverter.cs + + + root\PdfSharpException.cs + + + root\ProductVersionInfo.cs + + + root\PSSR.cs + + + root\VersionInfo.cs + + + SharpZipLib\Checksums\Adler32.cs + + + SharpZipLib\Checksums\CRC32.cs + + + SharpZipLib\Checksums\IChecksum.cs + + + SharpZipLib\SharpZipBaseException.cs + + + SharpZipLib\Zip\Compression\Deflater.cs + + + SharpZipLib\Zip\Compression\DeflaterConstants.cs + + + SharpZipLib\Zip\Compression\DeflaterEngine.cs + + + SharpZipLib\Zip\Compression\DeflaterHuffman.cs + + + SharpZipLib\Zip\Compression\DeflaterPending.cs + + + SharpZipLib\Zip\Compression\Inflater.cs + + + SharpZipLib\Zip\Compression\InflaterDynHeader.cs + + + SharpZipLib\Zip\Compression\InflaterHuffmanTree.cs + + + SharpZipLib\Zip\Compression\PendingBuffer.cs + + + SharpZipLib\Zip\Compression\Streams\DeflaterOutputStream.cs + + + SharpZipLib\Zip\Compression\Streams\InflaterInputStream.cs + + + SharpZipLib\Zip\Compression\Streams\OutputWindow.cs + + + SharpZipLib\Zip\Compression\Streams\StreamManipulator.cs + + + SharpZipLib\Zip\ZipConstants.cs + + + SharpZipLib\Zip\ZipException.cs + + + Forms\PagePreview.resx + PagePreview.cs + + + Forms\PagePreviewCanvas.resx + PagePreviewCanvas.cs + + + + + Resources\Messages.de.restext + + + Resources\Messages.restext + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + SharpZipLib\ReadMe.txt + + + + + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp-gdi/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp-gdi/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp-gdi/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp-wpf/PdfSharp-wpf.csproj b/src/PDFsharp/src/PdfSharp-wpf/PdfSharp-wpf.csproj new file mode 100644 index 00000000..21e1c3b1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-wpf/PdfSharp-wpf.csproj @@ -0,0 +1,1206 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D} + Library + Properties + PdfSharp + PdfSharp-wpf + v3.5 + 512 + true + StrongnameKey.snk + + + 3.5 + + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + SAK + SAK + SAK + SAK + + + + + true + full + false + bin\Debug\ + TRACE;DEBUG;WPF + prompt + 4 + false + + + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE;WPF + prompt + 4 + true + AllRules.ruleset + bin\Release\PdfSharp-wpf.xml + false + + + + 3.0 + + + 3.0 + + + + + + + 3.0 + + + + + !internal\Configuration.cs + + + !internal\Directives.cs + + + !internal\TargetContext.cs + + + Drawing.BarCodes\BarCode.cs + + + Drawing.BarCodes\BarCodeRenderInfo.cs + + + Drawing.BarCodes\BcgSR.cs + + + Drawing.BarCodes\Code2of5Interleaved.cs + + + Drawing.BarCodes\Code3of9Standard.cs + + + Drawing.BarCodes\CodeBase.cs + + + Drawing.BarCodes\CodeDataMatrix.cs + + + Drawing.BarCodes\CodeOmr.cs + + + Drawing.BarCodes\DataMatrixImage.cs + + + Drawing.BarCodes\enums\AnchorType.cs + + + Drawing.BarCodes\enums\CodeDirection.cs + + + Drawing.BarCodes\enums\CodeType.cs + + + Drawing.BarCodes\enums\DataMatrixEncoding.cs + + + Drawing.BarCodes\enums\MarkDistance.cs + + + Drawing.BarCodes\enums\TextLocation.cs + + + Drawing.BarCodes\MatrixCode.cs + + + Drawing.BarCodes\OmrData.cs + + + Drawing.BarCodes\ThickThinBarcodeRenderer.cs + + + Drawing.Internal\IImageImporter.cs + + + Drawing.Internal\ImageImporter.cs + + + Drawing.Internal\ImageImporterBmp.cs + + + Drawing.Internal\ImageImporterJpeg.cs + + + Drawing.Internal\ImageImporterRoot.cs + + + Drawing.Layout\enums\XParagraphAlignment.cs + + + Drawing.Layout\XTextFormatter.cs + + + Drawing.Pdf\enums\DirtyFlags.cs + + + Drawing.Pdf\enums\StreamMode.cs + + + Drawing.Pdf\PdfGraphicsState.cs + + + Drawing.Pdf\XGraphicsPdfRenderer.cs + + + Drawing\CoreGraphicsPath.cs + + + Drawing\enums\PathStart.cs + + + Drawing\enums\XColorSpace.cs + + + Drawing\enums\XCombineMode.cs + + + Drawing\enums\XDashStyle.cs + + + Drawing\enums\XFillMode.cs + + + Drawing\enums\XFontStyle.cs + + + Drawing\enums\XGdiFontStyle.cs + + + Drawing\enums\XGraphicRenderTarget.cs + + + Drawing\enums\XGraphicsPathItemType.cs + + + Drawing\enums\XGraphicsPdfPageOptions.cs + + + Drawing\enums\XGraphicsUnit.cs + + + Drawing\enums\XKnownColor.cs + + + Drawing\enums\XLineAlignment.cs + + + Drawing\enums\XLinearGradientMode.cs + + + Drawing\enums\XLineCap.cs + + + Drawing\enums\XLineJoin.cs + + + Drawing\enums\XMatrixOrder.cs + + + Drawing\enums\XPageDirection.cs + + + Drawing\enums\XSmoothingMode.cs + + + Drawing\enums\XStringAlignment.cs + + + Drawing\enums\XStyleSimulations.cs + + + Drawing\enums\XSweepDirection.cs + + + Drawing\FontFamilyCache.cs + + + Drawing\FontFamilyInternal.cs + + + Drawing\FontHelper.cs + + + Drawing\GeometryHelper.cs + + + Drawing\GraphicsStateStack.cs + + + Drawing\ImageHelper.cs + + + Drawing\InternalGraphicsState.cs + + + Drawing\IXGraphicsRenderer.cs + + + Drawing\PdfFontOptions.cs + + + Drawing\XBitmapDecoder.cs + + + Drawing\XBitmapEncoder.cs + + + Drawing\XBitmapImage.cs + + + Drawing\XBitmapSource.cs + + + Drawing\XBrush.cs + + + Drawing\XBrushes.cs + + + Drawing\XColor.cs + + + Drawing\XColorResourceManager.cs + + + Drawing\XColors.cs + + + Drawing\XConvert.cs + + + Drawing\XFont.cs + + + Drawing\XFontFamily.cs + + + Drawing\XFontMetrics.cs + + + Drawing\XFontSource.cs + + + Drawing\XFontStretch.cs + + + Drawing\XFontWeight.cs + + + Drawing\XFontWeights.cs + + + Drawing\XForm.cs + + + Drawing\XGlyphTypeface.cs + + + Drawing\XGradientBrush.cs + + + Drawing\XGraphics.cs + + + Drawing\XGraphicsContainer.cs + + + Drawing\XGraphicsPath.cs + + + Drawing\XGraphicsPathInternals.cs + + + Drawing\XGraphicsPathItem.cs + + + Drawing\XGraphicsState.cs + + + Drawing\XImage.cs + + + Drawing\XImageFormat.cs + + + Drawing\XKnownColorTable.cs + + + Drawing\XLinearGradientBrush.cs + + + Drawing\XMatrix.cs + + + Drawing\XPdfForm.cs + + + Drawing\XPen.cs + + + Drawing\XPens.cs + + + Drawing\XPoint.cs + + + Drawing\XPrivateFontCollection.cs + + + Drawing\XRadialGradientBrush.cs + + + Drawing\XRect.cs + + + Drawing\XSize.cs + + + Drawing\XSolidBrush.cs + + + Drawing\XStringFormat.cs + + + Drawing\XStringFormats.cs + + + Drawing\XTypeface.cs + + + Drawing\XUnit.cs + + + Drawing\XVector.cs + + + Events\DocumentEvents.cs + + + Fonts.OpenType\enums\FontTechnology.cs + + + Fonts.OpenType\enums\TableTagNames.cs + + + Fonts.OpenType\FontDescriptor.cs + + + Fonts.OpenType\GenericFontTable.cs + + + Fonts.OpenType\GlyphDataTable.cs + + + Fonts.OpenType\GlyphTypefaceCache.cs + + + Fonts.OpenType\IndexToLocationTable.cs + + + Fonts.OpenType\IRefFontTable.cs + + + Fonts.OpenType\OpenTypeDescriptor.cs + + + Fonts.OpenType\OpenTypeFontface.cs + + + Fonts.OpenType\OpenTypeFontfaceCache.cs + + + Fonts.OpenType\OpenTypeFontTable.cs + + + Fonts.OpenType\OpenTypeFontTables.cs + + + Fonts.OpenType\OpenTypeFontWriter.cs + + + Fonts.OpenType\TableDirectoryEntry.cs + + + Fonts\AdobeGlyphList20.cs + + + Fonts\AdobeGlyphListForNewFonts.cs + + + Fonts\CMapInfo.cs + + + Fonts\FontDescriptorCache.cs + + + Fonts\FontFactory.cs + + + Fonts\FontResolverInfo.cs + + + Fonts\FontResolvingOptions.cs + + + Fonts\FontWriter.cs + + + Fonts\GlobalFontSettings.cs + + + Fonts\IFontResolver.cs + + + Fonts\PlatformFontResolver.cs + + + Fonts\PlatformFontResolverInfo.cs + + + Internal\Calc.cs + + + Internal\ColorHelper.cs + + + Internal\Diagnostics.cs + + + Internal\DiagnosticsHelper.cs + + + Internal\DoubleUtil.cs + + + Internal\Lock.cs + + + Internal\NativeMethods.cs + + + Internal\TokenizerHelper.cs + + + Pdf.AcroForms\enums\PdfAcroFieldFlags.cs + + + Pdf.AcroForms\PdfAcroField.cs + + + Pdf.AcroForms\PdfAcroForm.cs + + + Pdf.AcroForms\PdfButtonField.cs + + + Pdf.AcroForms\PdfCheckBoxField.cs + + + Pdf.AcroForms\PdfChoiceField.cs + + + Pdf.AcroForms\PdfComboBoxField.cs + + + Pdf.AcroForms\PdfGenericField.cs + + + Pdf.AcroForms\PdfListBoxField.cs + + + Pdf.AcroForms\PdfPushButtonField.cs + + + Pdf.AcroForms\PdfRadioButtonField.cs + + + Pdf.AcroForms\PdfSignatureField.cs + + + Pdf.AcroForms\PdfTextField.cs + + + Pdf.Actions\enums\PdfNamedActionNames.cs + + + Pdf.Actions\PdfAction.cs + + + Pdf.Actions\PdfEmbeddedGoToAction.cs + + + Pdf.Actions\PdfRemoteGoToAction.cs + + + Pdf.Actions\PdfGoToAction.cs + + + Pdf.Advanced\IContentStream.cs + + + Pdf.Advanced\PdfCatalog.cs + + + Pdf.Advanced\PdfCIDFont.cs + + + Pdf.Advanced\PdfContent.cs + + + Pdf.Advanced\PdfContents.cs + + + Pdf.Advanced\PdfCrossReferenceStream.cs + + + Pdf.Advanced\PdfCrossReferenceTable.cs + + + Pdf.Advanced\PdfDictionaryWithContentStream.cs + + + Pdf.Advanced\PdfEmbeddedFileStream.cs + + + Pdf.Advanced\PdfExtGState.cs + + + Pdf.Advanced\PdfExtGStateTable.cs + + + Pdf.Advanced\PdfFileSpecification.cs + + + Pdf.Advanced\PdfFont.cs + + + Pdf.Advanced\PdfFontDescriptor.cs + + + Pdf.Advanced\PdfFontTable.cs + + + Pdf.Advanced\PdfFormXObject.cs + + + Pdf.Advanced\PdfFormXObjectTable.cs + + + Pdf.Advanced\PdfGroupAttributes.cs + + + Pdf.Advanced\PdfImage.cs + + + Pdf.Advanced\PdfImage.FaxEncode.cs + + + Pdf.Advanced\PdfImageTable.cs + + + Pdf.Advanced\PdfImportedObjectTable.cs + + + Pdf.Advanced\PdfInternals.cs + + + Pdf.Advanced\PdfNamedDestinationParameters.cs + + + Pdf.Advanced\PdfNameDictionary.cs + + + Pdf.Advanced\PdfObjectInternals.cs + + + Pdf.Advanced\PdfObjectStream.cs + + + Pdf.Advanced\PdfPageInheritableObjects.cs + + + Pdf.Advanced\PdfReference.cs + + + Pdf.Advanced\PdfResourceMap.cs + + + Pdf.Advanced\PdfResources.cs + + + Pdf.Advanced\PdfResourceTable.cs + + + Pdf.Advanced\PdfShading.cs + + + Pdf.Advanced\PdfShadingPattern.cs + + + Pdf.Advanced\PdfSoftMask.cs + + + Pdf.Advanced\PdfTilingPattern.cs + + + Pdf.Advanced\PdfToUnicodeMap.cs + + + Pdf.Advanced\PdfTrailer.cs + + + Pdf.Advanced\PdfTransparencyGroupAttributes.cs + + + Pdf.Advanced\PdfTrueTypeFont.cs + + + Pdf.Advanced\PdfType0Font.cs + + + Pdf.Advanced\PdfType1Font.cs + + + Pdf.Advanced\PdfXObject.cs + + + Pdf.Annotations\enums\PdfAnnotationFlags.cs + + + Pdf.Annotations\enums\PdfRubberStampAnnotationIcon.cs + + + Pdf.Annotations\enums\PdfTextAnnotationIcon.cs + + + Pdf.Annotations\PdfAnnotation.cs + + + Pdf.Annotations\PdfAnnotations.cs + + + Pdf.Annotations\PdfGenericAnnotation.cs + + + Pdf.Annotations\PdfLinkAnnotation.cs + + + Pdf.Annotations\PdfRubberStampAnnotation.cs + + + Pdf.Annotations\PdfTextAnnotation.cs + + + Pdf.Annotations\PdfWidgetAnnotation.cs + + + Pdf.Content.Objects\CObjects.cs + + + Pdf.Content.Objects\enum\OpCodeFlags.cs + + + Pdf.Content.Objects\enum\OpCodeName.cs + + + Pdf.Content.Objects\Operators.cs + + + Pdf.Content\Chars.cs + + + Pdf.Content\CLexer.cs + + + Pdf.Content\ContentReader.cs + + + Pdf.Content\ContentReaderException.cs + + + Pdf.Content\ContentWriter.cs + + + Pdf.Content\CParser.cs + + + Pdf.Content\enums\Symbol.cs + + + Pdf.Filters\Ascii85Decode.cs + + + Pdf.Filters\AsciiHexDecode.cs + + + Pdf.Filters\Filter.cs + + + Pdf.Filters\Filtering.cs + + + Pdf.Filters\FlateDecode.cs + + + Pdf.Filters\LzwDecode.cs + + + Pdf.Internal\AnsiEncoding.cs + + + Pdf.Internal\ColorSpaceHelper.cs + + + Pdf.Internal\DocEncoding.cs + + + Pdf.Internal\GlobalObjectTable.cs + + + Pdf.Internal\PdfDiagnostics.cs + + + Pdf.Internal\PdfEncoders.cs + + + Pdf.Internal\RawEncoding.cs + + + Pdf.Internal\RawUnicodeEncoding.cs + + + Pdf.Internal\ThreadLocalStorage.cs + + + Pdf.IO\Chars.cs + + + Pdf.IO\enums\PasswordValidity.cs + + + Pdf.IO\enums\PdfDocumentOpenMode.cs + + + Pdf.IO\enums\PdfWriterLayout.cs + + + Pdf.IO\enums\PdfWriterOptions.cs + + + Pdf.IO\enums\Symbol.cs + + + Pdf.IO\Lexer.cs + + + Pdf.IO\Parser.cs + + + Pdf.IO\PdfReader.cs + + + Pdf.IO\PdfReaderException.cs + + + Pdf.IO\PdfWriter.cs + + + Pdf.IO\ShiftStack.cs + + + Pdf.Printing\PdfFilePrinter.cs + + + Pdf.Security\enums\PdfDocumentSecurity.cs + + + Pdf.Security\enums\PdfUserAccessPermission.cs + + + Pdf.Security\MD5Managed.cs + + + Pdf.Security\PdfSecurityHandler.cs + + + Pdf.Security\PdfSecuritySettings.cs + + + Pdf.Security\PdfStandardSecurityHandler.cs + + + Pdf.Structure\PdfAttributesBase.cs + + + Pdf.Structure\PdfStructureElement.cs + + + Pdf.Structure\PdfMarkedContentReference.cs + + + Pdf.Structure\PdfMarkInformation.cs + + + Pdf.Structure\PdfObjectReference.cs + + + Pdf.Structure\PdfStructureElement.cs + + + Pdf.Structure\PdfStructureTreeRoot.cs + + + Pdf.Structure\PdfTableAttributes.cs + + + Pdf\EntryInfoAttribute.cs + + + Pdf\enums\DocumentState.cs + + + Pdf\enums\PdfColorMode.cs + + + Pdf\enums\PdfCustomValueCompression.cs + + + Pdf\enums\PdfFlateEncodeMode.cs + + + Pdf\enums\PdfFontEmbedding.cs + + + Pdf\enums\PdfFontEncoding.cs + + + Pdf\enums\PdfOutlineStyle.cs + + + Pdf\enums\PdfPageDestinationType.cs + + + Pdf\enums\PdfPageLayout.cs + + + Pdf\enums\PdfPageMode.cs + + + Pdf\enums\PdfReadingDirection.cs + + + Pdf\enums\PdfTextStringEncoding.cs + + + Pdf\enums\PdfUseFlateDecoderForJpegImages.cs + + + Pdf\KeysBase.cs + + + Pdf\KeysMeta.cs + + + Pdf\PdfArray.cs + + + Pdf\PdfBoolean.cs + + + Pdf\PdfBooleanObject.cs + + + Pdf\PdfCustomValue.cs + + + Pdf\PdfCustomValues.cs + + + Pdf\PdfNameTreeNode.cs + + + Pdf\PdfDate.cs + + + Pdf\PdfDictionary.cs + + + Pdf\PdfDocument.cs + + + Pdf\PdfDocumentInformation.cs + + + Pdf\PdfDocumentOptions.cs + + + Pdf\PdfDocumentSettings.cs + + + Pdf\PdfInteger.cs + + + Pdf\PdfIntegerObject.cs + + + Pdf\PdfItem.cs + + + Pdf\PdfLiteral.cs + + + Pdf\PdfMetadata.cs + + + Pdf\PdfName.cs + + + Pdf\PdfNameObject.cs + + + Pdf\PdfNull.cs + + + Pdf\PdfNullObject.cs + + + Pdf\PdfNumber.cs + + + Pdf\PdfNumberObject.cs + + + Pdf\PdfNumberTreeNode.cs + + + Pdf\PdfObject.cs + + + Pdf\PdfObjectID.cs + + + Pdf\PdfOutline.cs + + + Pdf\PdfOutlineCollection.cs + + + Pdf\PdfPage.cs + + + Pdf\PdfPages.cs + + + Pdf\PdfReal.cs + + + Pdf\PdfRealObject.cs + + + Pdf\PdfRectangle.cs + + + Pdf\PdfReferenceTable.cs + + + Pdf\PdfString.cs + + + Pdf\PdfStringObject.cs + + + Pdf\PdfUInteger.cs + + + Pdf\PdfUIntegerObject.cs + + + Pdf\PdfViewerPreferences.cs + + + Pdf\TrimMargins.cs + + + Properties\AssemblyInfo.cs + + + root\enums\PageOrientation.cs + + + root\enums\PageSize.cs + + + root\enums\PSMsgID.cs + + + root\PageSizeConverter.cs + + + root\PdfSharpException.cs + + + root\ProductVersionInfo.cs + + + root\PSSR.cs + + + root\VersionInfo.cs + + + SharpZipLib\Checksums\Adler32.cs + + + SharpZipLib\Checksums\CRC32.cs + + + SharpZipLib\Checksums\IChecksum.cs + + + SharpZipLib\SharpZipBaseException.cs + + + SharpZipLib\Zip\Compression\Deflater.cs + + + SharpZipLib\Zip\Compression\DeflaterConstants.cs + + + SharpZipLib\Zip\Compression\DeflaterEngine.cs + + + SharpZipLib\Zip\Compression\DeflaterHuffman.cs + + + SharpZipLib\Zip\Compression\DeflaterPending.cs + + + SharpZipLib\Zip\Compression\Inflater.cs + + + SharpZipLib\Zip\Compression\InflaterDynHeader.cs + + + SharpZipLib\Zip\Compression\InflaterHuffmanTree.cs + + + SharpZipLib\Zip\Compression\PendingBuffer.cs + + + SharpZipLib\Zip\Compression\Streams\DeflaterOutputStream.cs + + + SharpZipLib\Zip\Compression\Streams\InflaterInputStream.cs + + + SharpZipLib\Zip\Compression\Streams\OutputWindow.cs + + + SharpZipLib\Zip\Compression\Streams\StreamManipulator.cs + + + SharpZipLib\Zip\ZipConstants.cs + + + SharpZipLib\Zip\ZipException.cs + + + Windows\enums\RenderMode.cs + + + Windows\enums\Zoom.cs + + + Windows\VisualPresenter.cs + + + PagePreview.xaml + + + + + + Resources\Messages.de.restext + + + Resources\Messages.restext + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + SharpZipLib\ReadMe.txt + + + + + MSBuild:Compile + Designer + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp-wpf/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp-wpf/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp-wpf/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml new file mode 100644 index 00000000..6ad458f8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml.cs b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml.cs new file mode 100644 index 00000000..bef67d30 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreview.xaml.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +using PdfSharp.Drawing; + +namespace PdfSharp.Windows +{ + /// + /// A simple page viewer for WPF and Silverlight. + /// Not based on empira application framework. + /// + public partial class PagePreview : UserControl + { + /// + /// Initializes a new instance of the class. + /// + public PagePreview() + { + InitializeComponent(); + + //TextBlock tb = new TextBlock(); + //Binding b = new Binding("FontSize"); + //b.Source = this; + //tb.SetBinding(TextBlock.FontSizeProperty, b); + + //canvasGrid.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvasGrid.SetBinding(HeightProperty, new Binding("CanvasHeight")); + //canvas.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvas.SetBinding(HeightProperty, new Binding("CanvasHeight")); + + LayoutRoot.DataContext = this; + Zoom = (Zoom)100; + } + + void Test() + { + double factor = 1; + int zoom = (int)Zoom; + if (zoom > 0) + factor = Math.Max(Math.Min(zoom, 800), 10) / 100.0; + else + factor = 1; + + canvasGrid.Width = 480 * factor; + canvasGrid.Height = 640 * factor; + scaleTransform.ScaleX = factor; + scaleTransform.ScaleY = factor; + + //CanvasWidth = 480 * factor; + //CanvasHeight = 640 * factor; + //CanvasScaleX = 1 * factor; + //CanvasScaleY = 1 * factor; + } + + /// + /// Gets the canvas. + /// + public Canvas Canvas + { + get { return canvas; } + } + + /// + /// Sets the render function. + /// + public void SetRenderFunction(Action renderFunction) + { + if (canvas.Children.Count > 0) + canvas.Children.Clear(); + + XGraphics gfx = XGraphics.FromCanvas(canvas, new XSize(100, 100), XGraphicsUnit.Presentation); + renderFunction(gfx); + gfx.Dispose(); // necessary in WPF + } + + /// + /// Gets or sets the size of the page 1/96 inch. + /// + public Size PageSize + { + get { return (Size)GetValue(PageSizeProperty); } + set { SetValue(PageSizeProperty, value); } + } + + /// + /// Gets or sets the XGraphicsUnit of the page. + /// The default value is XGraphicsUnit.Point. + /// + public XGraphicsUnit PageGraphicsUnit + { + get { return _pageGraphicsUnit; } + set { _pageGraphicsUnit = value; } + } + XGraphicsUnit _pageGraphicsUnit = XGraphicsUnit.Point; + + + /// + /// DependencyProperty of PageSize. + /// + public readonly DependencyProperty PageSizeProperty = + DependencyProperty.Register("PageSize", typeof(Size), typeof(PagePreview), new PropertyMetadata(new Size(480, 640), PageSizeChanged)); + + private static void PageSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + + /// + /// Gets or sets the zoom. + /// + public Zoom Zoom + { + get { return (Zoom)GetValue(ZoomProperty); } + set { SetValue(ZoomProperty, value); } + } + + /// + /// DependencyProperty of Zoom. + /// + public readonly DependencyProperty ZoomProperty = + DependencyProperty.Register("Zoom", typeof(Zoom), typeof(PagePreview), new PropertyMetadata(Zoom.FullPage, ZoomChanged)); + + private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + +#if true_ + public double CanvasWidth + { + get { return (double)GetValue(CanvasWidthProperty); } + set { SetValue(CanvasWidthProperty, value); } + } + public readonly DependencyProperty CanvasWidthProperty = + DependencyProperty.Register("CanvasWidth", typeof(double), typeof(PagePreview), new PropertyMetadata(210.0)); + + public double CanvasHeight + { + get { return (double)GetValue(CanvasHeightProperty); } + set { SetValue(CanvasHeightProperty, value); } + } + public readonly DependencyProperty CanvasHeightProperty = + DependencyProperty.Register("CanvasHeight", typeof(double), typeof(PagePreview), new PropertyMetadata(297.0)); + + public double CanvasScaleX + { + get { return (double)GetValue(CanvasScaleXProperty); } + set { SetValue(CanvasScaleXProperty, value); } + } + public readonly DependencyProperty CanvasScaleXProperty = + DependencyProperty.Register("CanvasScaleX", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); + + public double CanvasScaleY + { + get { return (double)GetValue(CanvasScaleYProperty); } + set { SetValue(CanvasScaleYProperty, value); } + } + public readonly DependencyProperty CanvasScaleYProperty = + DependencyProperty.Register("CanvasScaleY", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); +#endif + + /// + /// Gets or sets the page visibility. + /// + public Visibility PageVisibility + { + get { return (Visibility)GetValue(PageVisibilityProperty); } + set { SetValue(PageVisibilityProperty, value); } + } + + /// + /// DependencyProperty of PageVisibility. + /// + public readonly DependencyProperty PageVisibilityProperty = + DependencyProperty.Register("PageVisibility", typeof(Visibility), typeof(PagePreview), null); + } +} diff --git a/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreviewDesignTimeData.cs b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreviewDesignTimeData.cs new file mode 100644 index 00000000..89cb769d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp-wpf/Windows/PagePreviewDesignTimeData.cs @@ -0,0 +1,30 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PdfSharp.Windows +{ +#if true_ + public class PagePreviewDesignTimeData + { + public double CanvasWidth + { + get { return 210; } + set { } + } + + public double CanvasHeight + { + get { return 297; } + set { } + } + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp.Charting-gdi/PdfSharp.Charting-gdi.csproj b/src/PDFsharp/src/PdfSharp.Charting-gdi/PdfSharp.Charting-gdi.csproj new file mode 100644 index 00000000..98b22b7f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting-gdi/PdfSharp.Charting-gdi.csproj @@ -0,0 +1,417 @@ + + + + Local + 9.0.30729 + 2.0 + {CFDB7D46-EA8D-47DE-B10A-9E755A1B48BA} + Debug + AnyCPU + + + + + PdfSharp.Charting-gdi + StrongnameKey.snk + JScript + Grid + IE50 + false + Library + PdfSharp + OnBuildSuccess + + + + + + + true + 3.5 + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;GDI + + + true + 4096 + false + + + false + false + false + true + 4 + full + prompt + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + TRACE;GDI + bin\Release\PdfSharp.Charting-gdi.xml + false + 4096 + false + + + true + false + false + true + 4 + none + prompt + AllRules.ruleset + + + + System + + + + + + Charting.Renderers\AreaChartRenderer.cs + + + Charting.Renderers\AreaPlotAreaRenderer.cs + + + Charting.Renderers\AxisRenderer.cs + + + Charting.Renderers\AxisTitleRenderer.cs + + + Charting.Renderers\BarChartRenderer.cs + + + Charting.Renderers\BarClusteredLegendRenderer.cs + + + Charting.Renderers\BarClusteredPlotAreaRenderer.cs + + + Charting.Renderers\BarDataLabelRenderer.cs + + + Charting.Renderers\BarGridlinesRenderer.cs + + + Charting.Renderers\BarPlotAreaRenderer.cs + + + Charting.Renderers\BarStackedPlotAreaRenderer.cs + + + Charting.Renderers\ChartRenderer.cs + + + Charting.Renderers\Colors.cs + + + Charting.Renderers\ColumnChartRenderer.cs + + + Charting.Renderers\ColumnClusteredPlotAreaRenderer.cs + + + Charting.Renderers\ColumnDataLabelRenderer.cs + + + Charting.Renderers\ColumnLikeChartRenderer.cs + + + Charting.Renderers\ColumnLikeGridlinesRenderer.cs + + + Charting.Renderers\ColumnLikeLegendRenderer.cs + + + Charting.Renderers\ColumnLikePlotAreaRenderer.cs + + + Charting.Renderers\ColumnPlotAreaRenderer.cs + + + Charting.Renderers\ColumnStackedPlotAreaRenderer.cs + + + Charting.Renderers\CombinationChartRenderer.cs + + + Charting.Renderers\Converter.cs + + + Charting.Renderers\DataLabelRenderer.cs + + + Charting.Renderers\GridlinesRenderer.cs + + + Charting.Renderers\HorizontalStackedYAxisRenderer.cs + + + Charting.Renderers\HorizontalXAxisRenderer.cs + + + Charting.Renderers\HorizontalYAxisRenderer.cs + + + Charting.Renderers\LegendEntryRenderer.cs + + + Charting.Renderers\LegendRenderer.cs + + + Charting.Renderers\LineChartRenderer.cs + + + Charting.Renderers\LineFormatRenderer.cs + + + Charting.Renderers\LinePlotAreaRenderer.cs + + + Charting.Renderers\MarkerRenderer.cs + + + Charting.Renderers\PieChartRenderer.cs + + + Charting.Renderers\PieClosedPlotAreaRenderer.cs + + + Charting.Renderers\PieDataLabelRenderer.cs + + + Charting.Renderers\PieExplodedPlotAreaRenderer.cs + + + Charting.Renderers\PieLegendRenderer.cs + + + Charting.Renderers\PiePlotAreaRenderer.cs + + + Charting.Renderers\PlotAreaBorderRenderer.cs + + + Charting.Renderers\PlotAreaRenderer.cs + + + Charting.Renderers\Renderer.cs + + + Charting.Renderers\RendererInfo.cs + + + Charting.Renderers\RendererParameters.cs + + + Charting.Renderers\VerticalStackedYAxisRenderer.cs + + + Charting.Renderers\VerticalXAxisRenderer.cs + + + Charting.Renderers\VerticalYAxisRenderer.cs + + + Charting.Renderers\WallRenderer.cs + + + Charting.Renderers\XAxisRenderer.cs + + + Charting.Renderers\YAxisRenderer.cs + + + Charting\Axis.cs + + + Charting\AxisTitle.cs + + + Charting\Chart.cs + + + Charting\ChartFrame.cs + + + Charting\ChartObject.cs + + + Charting\DataLabel.cs + + + Charting\DocumentObject.cs + + + Charting\DocumentObjectCollection.cs + + + Charting\enums\BlankType.cs + + + Charting\enums\ChartType.cs + + + Charting\enums\DataLabelPosition.cs + + + Charting\enums\DataLabelType.cs + + + Charting\enums\DockingType.cs + + + Charting\enums\FontProperties.cs + + + Charting\enums\HorizontalAlignment.cs + + + Charting\enums\LineStyle.cs + + + Charting\enums\MarkerStyle.cs + + + Charting\enums\TickMarkType.cs + + + Charting\enums\Underline.cs + + + Charting\enums\VerticalAlignment.cs + + + Charting\FillFormat.cs + + + Charting\Font.cs + + + Charting\Gridlines.cs + + + Charting\Legend.cs + + + Charting\LineFormat.cs + + + Charting\PlotArea.cs + + + Charting\Point.cs + + + Charting\PSCSR.cs + + + Charting\Series.cs + + + Charting\SeriesCollection.cs + + + Charting\SeriesElements.cs + + + Charting\TickLabels.cs + + + Charting\XSeries.cs + + + Charting\XSeriesElements.cs + + + Charting\XValue.cs + + + Charting\XValues.cs + + + Properties\AssemblyInfo.cs + + + root\VersionInfo.cs + + + Resources\Messages.de.restext + + + Resources\Messages.restext + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + {3f01920c-1117-4e1b-9c01-54eb82e5a1d1} + PdfSharp-gdi + + + + + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting-gdi/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp.Charting-gdi/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp.Charting-gdi/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp.Charting-wpf/PdfSharp.Charting-wpf.csproj b/src/PDFsharp/src/PdfSharp.Charting-wpf/PdfSharp.Charting-wpf.csproj new file mode 100644 index 00000000..4290aedc --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting-wpf/PdfSharp.Charting-wpf.csproj @@ -0,0 +1,420 @@ + + + + Local + 9.0.30729 + 2.0 + {E6A2734E-0CD6-4210-8AEC-47EE348F8D78} + Debug + AnyCPU + + + + + PdfSharp.Charting-wpf + StrongnameKey.snk + JScript + Grid + IE50 + false + Library + PdfSharp + OnBuildSuccess + + + + + + + true + 3.5 + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + SAK + SAK + SAK + SAK + + + + bin\Debug\ + false + 285212672 + false + + + TRACE;DEBUG;WPF + bin\Debug\PdfSharp.Charting-wpf.xml + true + 4096 + false + + + false + false + false + true + 4 + full + prompt + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + TRACE;WPF + bin\Release\PdfSharp.Charting-wpf.xml + true + 4096 + false + + + true + false + false + true + 4 + full + prompt + AllRules.ruleset + + + + + System + + + + + + Charting.Renderers\AreaChartRenderer.cs + + + Charting.Renderers\AreaPlotAreaRenderer.cs + + + Charting.Renderers\AxisRenderer.cs + + + Charting.Renderers\AxisTitleRenderer.cs + + + Charting.Renderers\BarChartRenderer.cs + + + Charting.Renderers\BarClusteredLegendRenderer.cs + + + Charting.Renderers\BarClusteredPlotAreaRenderer.cs + + + Charting.Renderers\BarDataLabelRenderer.cs + + + Charting.Renderers\BarGridlinesRenderer.cs + + + Charting.Renderers\BarPlotAreaRenderer.cs + + + Charting.Renderers\BarStackedPlotAreaRenderer.cs + + + Charting.Renderers\ChartRenderer.cs + + + Charting.Renderers\Colors.cs + + + Charting.Renderers\ColumnChartRenderer.cs + + + Charting.Renderers\ColumnClusteredPlotAreaRenderer.cs + + + Charting.Renderers\ColumnDataLabelRenderer.cs + + + Charting.Renderers\ColumnLikeChartRenderer.cs + + + Charting.Renderers\ColumnLikeGridlinesRenderer.cs + + + Charting.Renderers\ColumnLikeLegendRenderer.cs + + + Charting.Renderers\ColumnLikePlotAreaRenderer.cs + + + Charting.Renderers\ColumnPlotAreaRenderer.cs + + + Charting.Renderers\ColumnStackedPlotAreaRenderer.cs + + + Charting.Renderers\CombinationChartRenderer.cs + + + Charting.Renderers\Converter.cs + + + Charting.Renderers\DataLabelRenderer.cs + + + Charting.Renderers\GridlinesRenderer.cs + + + Charting.Renderers\HorizontalStackedYAxisRenderer.cs + + + Charting.Renderers\HorizontalXAxisRenderer.cs + + + Charting.Renderers\HorizontalYAxisRenderer.cs + + + Charting.Renderers\LegendEntryRenderer.cs + + + Charting.Renderers\LegendRenderer.cs + + + Charting.Renderers\LineChartRenderer.cs + + + Charting.Renderers\LineFormatRenderer.cs + + + Charting.Renderers\LinePlotAreaRenderer.cs + + + Charting.Renderers\MarkerRenderer.cs + + + Charting.Renderers\PieChartRenderer.cs + + + Charting.Renderers\PieClosedPlotAreaRenderer.cs + + + Charting.Renderers\PieDataLabelRenderer.cs + + + Charting.Renderers\PieExplodedPlotAreaRenderer.cs + + + Charting.Renderers\PieLegendRenderer.cs + + + Charting.Renderers\PiePlotAreaRenderer.cs + + + Charting.Renderers\PlotAreaBorderRenderer.cs + + + Charting.Renderers\PlotAreaRenderer.cs + + + Charting.Renderers\Renderer.cs + + + Charting.Renderers\RendererInfo.cs + + + Charting.Renderers\RendererParameters.cs + + + Charting.Renderers\VerticalStackedYAxisRenderer.cs + + + Charting.Renderers\VerticalXAxisRenderer.cs + + + Charting.Renderers\VerticalYAxisRenderer.cs + + + Charting.Renderers\WallRenderer.cs + + + Charting.Renderers\XAxisRenderer.cs + + + Charting.Renderers\YAxisRenderer.cs + + + Charting\Axis.cs + + + Charting\AxisTitle.cs + + + Charting\Chart.cs + + + Charting\ChartFrame.cs + + + Charting\ChartObject.cs + + + Charting\DataLabel.cs + + + Charting\DocumentObject.cs + + + Charting\DocumentObjectCollection.cs + + + Charting\enums\BlankType.cs + + + Charting\enums\ChartType.cs + + + Charting\enums\DataLabelPosition.cs + + + Charting\enums\DataLabelType.cs + + + Charting\enums\DockingType.cs + + + Charting\enums\FontProperties.cs + + + Charting\enums\HorizontalAlignment.cs + + + Charting\enums\LineStyle.cs + + + Charting\enums\MarkerStyle.cs + + + Charting\enums\TickMarkType.cs + + + Charting\enums\Underline.cs + + + Charting\enums\VerticalAlignment.cs + + + Charting\FillFormat.cs + + + Charting\Font.cs + + + Charting\Gridlines.cs + + + Charting\Legend.cs + + + Charting\LineFormat.cs + + + Charting\PlotArea.cs + + + Charting\Point.cs + + + Charting\PSCSR.cs + + + Charting\Series.cs + + + Charting\SeriesCollection.cs + + + Charting\SeriesElements.cs + + + Charting\TickLabels.cs + + + Charting\XSeries.cs + + + Charting\XSeriesElements.cs + + + Charting\XValue.cs + + + Charting\XValues.cs + + + Properties\AssemblyInfo.cs + + + root\VersionInfo.cs + + + + + Resources\Messages.de.restext + + + Resources\Messages.restext + + + StrongnameKey.snk + + + + + + {02FCC0BB-2AA2-43BA-8D2F-66D168B87A1D} + PdfSharp-wpf + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting-wpf/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp.Charting-wpf/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp.Charting-wpf/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaChartRenderer.cs new file mode 100644 index 00000000..331db180 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaChartRenderer.cs @@ -0,0 +1,189 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents an area chart renderer. + /// + internal class AreaChartRenderer : ColumnLikeChartRenderer + { + /// + /// Initializes a new instance of the AreaChartRenderer class with the + /// specified renderer parameters. + /// + internal AreaChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = new ChartRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeriesRendererInfo(); + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + cri.xAxisRendererInfo = (AxisRendererInfo)xar.Init(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + cri.yAxisRendererInfo = (AxisRendererInfo)yar.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + PlotAreaRenderer renderer = new AreaPlotAreaRenderer(_rendererParms); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)renderer.Init(); + + return cri; + } + + /// + /// Layouts and calculates the space used by the line chart. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Format(); + + // axes + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Format(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Format(); + + // Calculate rects and positions. + CalcLayout(); + + // Calculated remaining plot area, now it's safe to format. + PlotAreaRenderer renderer = new AreaPlotAreaRenderer(_rendererParms); + renderer.Format(); + } + + /// + /// Draws the column chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Draw(); + + // Draw wall. + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + // Draw gridlines. + GridlinesRenderer glr = new ColumnLikeGridlinesRenderer(_rendererParms); + glr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + PlotAreaRenderer renderer = new AreaPlotAreaRenderer(_rendererParms); + renderer.Draw(); + + // Draw axes. + if (cri.xAxisRendererInfo._axis != null) + { + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Draw(); + } + + if (cri.yAxisRendererInfo._axis != null) + { + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Draw(); + } + } + + /// + /// Initializes all necessary data to draw a series for an area chart. + /// + private void InitSeriesRendererInfo() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + SeriesCollection seriesColl = cri._chart.SeriesCollection; + cri.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + sri._series = seriesColl[idx]; + cri.seriesRendererInfos[idx] = sri; + } + + InitSeries(); + } + + /// + /// Initializes all necessary data to draw a series for an area chart. + /// + internal void InitSeries() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int seriesIndex = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, XColors.Black, ChartRenderer.DefaultSeriesLineWidth); + sri.FillFormat = Converter.ToXBrush(sri._series._fillFormat, ColumnColors.Item(seriesIndex++)); + + sri._pointRendererInfos = new PointRendererInfo[sri._series._seriesElements.Count]; + for (int pointIdx = 0; pointIdx < sri._pointRendererInfos.Length; ++pointIdx) + { + PointRendererInfo pri = new PointRendererInfo(); + Point point = sri._series._seriesElements[pointIdx]; + pri.Point = point; + if (point != null) + { + pri.LineFormat = sri.LineFormat; + pri.FillFormat = sri.FillFormat; + if (point._lineFormat != null && !point._lineFormat._color.IsEmpty) + pri.LineFormat = new XPen(point._lineFormat._color, point._lineFormat._width); + if (point._fillFormat != null && !point._lineFormat._color.IsEmpty) + pri.FillFormat = new XSolidBrush(point._fillFormat._color); + } + sri._pointRendererInfos[pointIdx] = pri; + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaPlotAreaRenderer.cs new file mode 100644 index 00000000..2fc08469 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AreaPlotAreaRenderer.cs @@ -0,0 +1,85 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of areas. + /// + internal class AreaPlotAreaRenderer : ColumnLikePlotAreaRenderer + { + /// + /// Initializes a new instance of the AreaPlotAreaRenderer class + /// with the specified renderer parameters. + /// + internal AreaPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the content of the area plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; + if (plotAreaRect.IsEmpty) + return; + + XGraphics gfx = _rendererParms.Graphics; + XGraphicsState state = gfx.Save(); + //gfx.SetClip(plotAreaRect, XCombineMode.Intersect); + gfx.IntersectClip(plotAreaRect); + + XMatrix matrix = cri.plotAreaRendererInfo._matrix; + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + int count = sri._series.Elements.Count; + XPoint[] points = new XPoint[count + 2]; + points[0] = new XPoint(xMajorTick / 2, 0); + for (int idx = 0; idx < count; idx++) + { + double pointValue = sri._series.Elements[idx].Value; + if (double.IsNaN(pointValue)) + pointValue = 0; + points[idx + 1] = new XPoint(idx + xMajorTick / 2, pointValue); + } + points[count + 1] = new XPoint(count - 1 + xMajorTick / 2, 0); + matrix.TransformPoints(points); + gfx.DrawPolygon(sri.LineFormat, sri.FillFormat, points, XFillMode.Winding); + } + + //gfx.ResetClip(); + gfx.Restore(state); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisRenderer.cs new file mode 100644 index 00000000..01bdd15d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisRenderer.cs @@ -0,0 +1,189 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base for all specialized axis renderer. Initialization common too all + /// axis renderer should come here. + /// + internal abstract class AxisRenderer : Renderer + { + /// + /// Initializes a new instance of the AxisRenderer class with the specified renderer parameters. + /// + internal AxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Initializes the axis title of the rendererInfo. All missing font attributes will be taken + /// from the specified defaultFont. + /// + protected void InitAxisTitle(AxisRendererInfo rendererInfo, XFont defaultFont) + { + if (rendererInfo._axis._title != null) + { + AxisTitleRendererInfo atri = new AxisTitleRendererInfo(); + rendererInfo._axisTitleRendererInfo = atri; + + atri._axisTitle = rendererInfo._axis._title; + atri.AxisTitleText = rendererInfo._axis._title._caption; + atri.AxisTitleAlignment = rendererInfo._axis._title._alignment; + atri.AxisTitleVerticalAlignment = rendererInfo._axis._title._verticalAlignment; + atri.AxisTitleFont = Converter.ToXFont(rendererInfo._axis._title._font, defaultFont); + XColor fontColor = XColors.Black; + if (rendererInfo._axis._title._font != null && !rendererInfo._axis._title._font._color.IsEmpty) + fontColor = rendererInfo._axis._title._font._color; + atri.AxisTitleBrush = new XSolidBrush(fontColor); + atri.AxisTitleOrientation = rendererInfo._axis._title._orientation; + } + } + + /// + /// Initializes the tick labels of the rendererInfo. All missing font attributes will be taken + /// from the specified defaultFont. + /// + protected void InitTickLabels(AxisRendererInfo rendererInfo, XFont defaultFont) + { + if (rendererInfo._axis._tickLabels != null) + { + rendererInfo.TickLabelsFont = Converter.ToXFont(rendererInfo._axis._tickLabels._font, defaultFont); + XColor fontColor = XColors.Black; + if (rendererInfo._axis._tickLabels._font != null && !rendererInfo._axis._tickLabels._font._color.IsEmpty) + fontColor = rendererInfo._axis._tickLabels._font._color; + rendererInfo.TickLabelsBrush = new XSolidBrush(fontColor); + + rendererInfo.TickLabelsFormat = rendererInfo._axis._tickLabels._format; + if (rendererInfo.TickLabelsFormat == null) + rendererInfo.TickLabelsFormat = GetDefaultTickLabelsFormat(); + } + else + { + rendererInfo.TickLabelsFont = defaultFont; + rendererInfo.TickLabelsBrush = new XSolidBrush(XColors.Black); + rendererInfo.TickLabelsFormat = GetDefaultTickLabelsFormat(); + } + } + + /// + /// Initializes the line format of the rendererInfo. + /// + protected void InitAxisLineFormat(AxisRendererInfo rendererInfo) + { + if (rendererInfo._axis._minorTickMarkInitialized) + rendererInfo.MinorTickMark = rendererInfo._axis.MinorTickMark; + + if (rendererInfo._axis._majorTickMarkInitialized) + rendererInfo.MajorTickMark = rendererInfo._axis.MajorTickMark; + else + rendererInfo.MajorTickMark = TickMarkType.Outside; + + if (rendererInfo.MinorTickMark != TickMarkType.None) + rendererInfo.MinorTickMarkLineFormat = Converter.ToXPen(rendererInfo._axis._lineFormat, XColors.Black, DefaultMinorTickMarkLineWidth); + + if (rendererInfo.MajorTickMark != TickMarkType.None) + rendererInfo.MajorTickMarkLineFormat = Converter.ToXPen(rendererInfo._axis._lineFormat, XColors.Black, DefaultMajorTickMarkLineWidth); + + if (rendererInfo._axis._lineFormat != null) + { + rendererInfo.LineFormat = Converter.ToXPen(rendererInfo._axis.LineFormat, XColors.Black, DefaultLineWidth); + if (!rendererInfo._axis._majorTickMarkInitialized) + rendererInfo.MajorTickMark = TickMarkType.Outside; + } + } + + /// + /// Initializes the gridlines of the rendererInfo. + /// + protected void InitGridlines(AxisRendererInfo rendererInfo) + { + if (rendererInfo._axis._minorGridlines != null) + { + rendererInfo.MinorGridlinesLineFormat = + Converter.ToXPen(rendererInfo._axis._minorGridlines._lineFormat, XColors.Black, DefaultGridLineWidth); + } + else if (rendererInfo._axis._hasMinorGridlines) + { + // No minor gridlines object are given, but user asked for. + rendererInfo.MinorGridlinesLineFormat = new XPen(XColors.Black, DefaultGridLineWidth); + } + + if (rendererInfo._axis._majorGridlines != null) + { + rendererInfo.MajorGridlinesLineFormat = + Converter.ToXPen(rendererInfo._axis._majorGridlines._lineFormat, XColors.Black, DefaultGridLineWidth); + } + else if (rendererInfo._axis._hasMajorGridlines) + { + // No major gridlines object are given, but user asked for. + rendererInfo.MajorGridlinesLineFormat = new XPen(XColors.Black, DefaultGridLineWidth); + } + } + + /// + /// Default width for a variety of lines. + /// + protected const double DefaultLineWidth = 0.4; // 0.15 mm + + /// + /// Default width for a gridlines. + /// + protected const double DefaultGridLineWidth = 0.15; + + /// + /// Default width for major tick marks. + /// + protected const double DefaultMajorTickMarkLineWidth = 1; + + /// + /// Default width for minor tick marks. + /// + protected const double DefaultMinorTickMarkLineWidth = 1; + + /// + /// Default width of major tick marks. + /// + protected const double DefaultMajorTickMarkWidth = 4.3; // 1.5 mm + + /// + /// Default width of minor tick marks. + /// + protected const double DefaultMinorTickMarkWidth = 2.8; // 1 mm + + /// + /// Default width of space between label and tick mark. + /// + protected const double SpaceBetweenLabelAndTickmark = 2.1; // 0.7 mm + + protected abstract string GetDefaultTickLabelsFormat(); + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisTitleRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisTitleRenderer.cs new file mode 100644 index 00000000..8a13f849 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/AxisTitleRenderer.cs @@ -0,0 +1,182 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a axis title renderer used for x and y axis titles. + /// + internal class AxisTitleRenderer : Renderer + { + /// + /// Initializes a new instance of the AxisTitleRenderer class with the + /// specified renderer parameters. + /// + internal AxisTitleRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used for the axis title. + /// + internal override void Format() + { + XGraphics gfx = _rendererParms.Graphics; + + AxisTitleRendererInfo atri = ((AxisRendererInfo)_rendererParms.RendererInfo)._axisTitleRendererInfo; + if (atri.AxisTitleText != "") + { + XSize size = gfx.MeasureString(atri.AxisTitleText, atri.AxisTitleFont); + if (atri.AxisTitleOrientation != 0) + { + XPoint[] points = new XPoint[2]; + points[0].X = 0; + points[0].Y = 0; + points[1].X = size.Width; + points[1].Y = size.Height; + + XMatrix matrix = new XMatrix(); + matrix.RotatePrepend(-atri.AxisTitleOrientation); + matrix.TransformPoints(points); + + size.Width = Math.Abs(points[1].X - points[0].X); + size.Height = Math.Abs(points[1].Y - points[0].Y); + } + + atri.X = 0; + atri.Y = 0; + atri.Height = size.Height; + atri.Width = size.Width; + } + } + + /// + /// Draws the axis title. + /// + internal override void Draw() + { + AxisRendererInfo ari = (AxisRendererInfo)_rendererParms.RendererInfo; + AxisTitleRendererInfo atri = ari._axisTitleRendererInfo; + if (atri.AxisTitleText != "") + { + XGraphics gfx = _rendererParms.Graphics; + if (atri.AxisTitleOrientation != 0) + { + XRect layout = atri.Rect; + layout.X = -(layout.Width / 2); + layout.Y = -(layout.Height / 2); + + double x = 0; + switch (atri.AxisTitleAlignment) + { + case HorizontalAlignment.Center: + x = atri.X + atri.Width / 2; + break; + + case HorizontalAlignment.Right: + x = atri.X + atri.Width - layout.Width / 2; + break; + + case HorizontalAlignment.Left: + default: + x = atri.X; + break; + } + + double y = 0; + switch (atri.AxisTitleVerticalAlignment) + { + case VerticalAlignment.Center: + y = atri.Y + atri.Height / 2; + break; + + case VerticalAlignment.Bottom: + y = atri.Y + atri.Height - layout.Height / 2; + break; + + case VerticalAlignment.Top: + default: + y = atri.Y; + break; + } + + XStringFormat xsf = new XStringFormat(); + xsf.Alignment = XStringAlignment.Center; + xsf.LineAlignment = XLineAlignment.Center; + + XGraphicsState state = gfx.Save(); + gfx.TranslateTransform(x, y); + gfx.RotateTransform(-atri.AxisTitleOrientation); + gfx.DrawString(atri.AxisTitleText, atri.AxisTitleFont, atri.AxisTitleBrush, layout, xsf); + gfx.Restore(state); + } + else + { + XStringFormat format = new XStringFormat(); + switch (atri.AxisTitleAlignment) + { + case HorizontalAlignment.Center: + format.Alignment = XStringAlignment.Center; + break; + + case HorizontalAlignment.Right: + format.Alignment = XStringAlignment.Far; + break; + + case HorizontalAlignment.Left: + default: + format.Alignment = XStringAlignment.Near; + break; + } + + switch (atri.AxisTitleVerticalAlignment) + { + case VerticalAlignment.Center: + format.LineAlignment = XLineAlignment.Center; + break; + + case VerticalAlignment.Bottom: + format.LineAlignment = XLineAlignment.Far; + break; + + case VerticalAlignment.Top: + default: + format.LineAlignment = XLineAlignment.Near; + break; + } + + gfx.DrawString(atri.AxisTitleText, atri.AxisTitleFont, atri.AxisTitleBrush, atri.Rect, format); + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarChartRenderer.cs new file mode 100644 index 00000000..efa68ccc --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarChartRenderer.cs @@ -0,0 +1,257 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a bar chart renderer. + /// + internal class BarChartRenderer : ChartRenderer + { + /// + /// Initializes a new instance of the BarChartRenderer class with the + /// specified renderer parameters. + /// + internal BarChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = new ChartRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeriesRendererInfo(); + + LegendRenderer lr = GetLegendRenderer(); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + AxisRenderer xar = new VerticalXAxisRenderer(_rendererParms); + cri.xAxisRendererInfo = (AxisRendererInfo)xar.Init(); + + AxisRenderer yar = GetYAxisRenderer(); + cri.yAxisRendererInfo = (AxisRendererInfo)yar.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)renderer.Init(); + + DataLabelRenderer dlr = new BarDataLabelRenderer(_rendererParms); + dlr.Init(); + + return cri; + } + + /// + /// Layouts and calculates the space used by the column chart. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = GetLegendRenderer(); + lr.Format(); + + // axes + AxisRenderer xar = new VerticalXAxisRenderer(_rendererParms); + xar.Format(); + + AxisRenderer yar = GetYAxisRenderer(); + yar.Format(); + + // Calculate rects and positions. + XRect chartRect = LayoutLegend(); + cri.xAxisRendererInfo.X = chartRect.Left; + cri.xAxisRendererInfo.Y = chartRect.Top; + cri.xAxisRendererInfo.Height = chartRect.Height - cri.yAxisRendererInfo.Height; + cri.yAxisRendererInfo.X = chartRect.Left + cri.xAxisRendererInfo.Width; + cri.yAxisRendererInfo.Y = chartRect.Bottom - cri.yAxisRendererInfo.Height; + cri.yAxisRendererInfo.Width = chartRect.Width - cri.xAxisRendererInfo.Width; + cri.plotAreaRendererInfo.X = cri.yAxisRendererInfo.X; + cri.plotAreaRendererInfo.Y = cri.xAxisRendererInfo.Y; + cri.plotAreaRendererInfo.Width = cri.yAxisRendererInfo.InnerRect.Width; + cri.plotAreaRendererInfo.Height = cri.xAxisRendererInfo.Height; + + // Calculated remaining plot area, now it's safe to format. + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Format(); + + DataLabelRenderer dlr = new BarDataLabelRenderer(_rendererParms); + dlr.Format(); + } + + /// + /// Draws the column chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = GetLegendRenderer(); + lr.Draw(); + + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + GridlinesRenderer glr = new BarGridlinesRenderer(_rendererParms); + glr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Draw(); + + DataLabelRenderer dlr = new BarDataLabelRenderer(_rendererParms); + dlr.Draw(); + + if (cri.xAxisRendererInfo._axis != null) + { + AxisRenderer xar = new VerticalXAxisRenderer(_rendererParms); + xar.Draw(); + } + + if (cri.yAxisRendererInfo._axis != null) + { + AxisRenderer yar = GetYAxisRenderer(); + yar.Draw(); + } + } + + /// + /// Returns the specific plot area renderer. + /// + private PlotAreaRenderer GetPlotAreaRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Bar2D: + return new BarClusteredPlotAreaRenderer(_rendererParms); + + case ChartType.BarStacked2D: + return new BarStackedPlotAreaRenderer(_rendererParms); + } + return null; + } + + /// + /// Returns the specific legend renderer. + /// + private LegendRenderer GetLegendRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Bar2D: + return new BarClusteredLegendRenderer(_rendererParms); + + case ChartType.BarStacked2D: + return new ColumnLikeLegendRenderer(_rendererParms); + } + return null; + } + + /// + /// Returns the specific plot area renderer. + /// + private YAxisRenderer GetYAxisRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Bar2D: + return new HorizontalYAxisRenderer(_rendererParms); + + case ChartType.BarStacked2D: + return new HorizontalStackedYAxisRenderer(_rendererParms); + } + return null; + } + + /// + /// Initializes all necessary data to draw all series for a column chart. + /// + private void InitSeriesRendererInfo() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + SeriesCollection seriesColl = cri._chart.SeriesCollection; + cri.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + // Lowest series is the first, like in Excel + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + sri._series = seriesColl[idx]; + cri.seriesRendererInfos[idx] = sri; + } + + InitSeries(); + } + + /// + /// Initializes all necessary data to draw all series for a column chart. + /// + internal void InitSeries() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int seriesIndex = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, XColors.Black, DefaultSeriesLineWidth); + sri.FillFormat = Converter.ToXBrush(sri._series._fillFormat, ColumnColors.Item(seriesIndex++)); + + sri._pointRendererInfos = new ColumnRendererInfo[sri._series._seriesElements.Count]; + for (int pointIdx = 0; pointIdx < sri._pointRendererInfos.Length; ++pointIdx) + { + PointRendererInfo pri = new ColumnRendererInfo(); + Point point = sri._series._seriesElements[pointIdx]; + pri.Point = point; + if (point != null) + { + pri.LineFormat = sri.LineFormat; + pri.FillFormat = sri.FillFormat; + if (point._lineFormat != null && !point._lineFormat._color.IsEmpty) + pri.LineFormat = Converter.ToXPen(point._lineFormat, sri.LineFormat); + if (point._fillFormat != null && !point._fillFormat._color.IsEmpty) + pri.FillFormat = new XSolidBrush(point._fillFormat._color); + } + sri._pointRendererInfos[pointIdx] = pri; + } + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredLegendRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredLegendRenderer.cs new file mode 100644 index 00000000..92e40c21 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredLegendRenderer.cs @@ -0,0 +1,105 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the legend renderer specific to bar charts. + /// + internal class BarClusteredLegendRenderer : ColumnLikeLegendRenderer + { + /// + /// Initializes a new instance of the BarClusteredLegendRenderer class with the + /// specified renderer parameters. + /// + internal BarClusteredLegendRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the legend. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + LegendRendererInfo lri = cri.legendRendererInfo; + if (lri == null) + return; + + XGraphics gfx = _rendererParms.Graphics; + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + + LegendEntryRenderer ler = new LegendEntryRenderer(parms); + + bool verticalLegend = (lri._legend._docking == DockingType.Left || lri._legend._docking == DockingType.Right); + int paddingFactor = 1; + if (lri.BorderPen != null) + paddingFactor = 2; + XRect legendRect = lri.Rect; + legendRect.X += LeftPadding * paddingFactor; + if (verticalLegend) + legendRect.Y = legendRect.Bottom - BottomPadding * paddingFactor; + else + legendRect.Y += TopPadding * paddingFactor; + + foreach (LegendEntryRendererInfo leri in cri.legendRendererInfo.Entries) + { + if (verticalLegend) + legendRect.Y -= leri.Height; + + XRect entryRect = legendRect; + entryRect.Width = leri.Width; + entryRect.Height = leri.Height; + + leri.Rect = entryRect; + parms.RendererInfo = leri; + ler.Draw(); + + if (verticalLegend) + legendRect.Y -= EntrySpacing; + else + legendRect.X += entryRect.Width + EntrySpacing; + } + + // Draw border around legend + if (lri.BorderPen != null) + { + XRect borderRect = lri.Rect; + borderRect.X += LeftPadding; + borderRect.Y += TopPadding; + borderRect.Width -= LeftPadding + RightPadding; + borderRect.Height -= TopPadding + BottomPadding; + gfx.DrawRectangle(lri.BorderPen, borderRect); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredPlotAreaRenderer.cs new file mode 100644 index 00000000..0b57ff10 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarClusteredPlotAreaRenderer.cs @@ -0,0 +1,127 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of clustered bars, i. e. all bars are drawn side by side. + /// + internal class BarClusteredPlotAreaRenderer : BarPlotAreaRenderer + { + /// + /// Initializes a new instance of the BarClusteredPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal BarClusteredPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the position, width and height of each bar of all series. + /// + protected override void CalcBars() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + double xMax = cri.xAxisRendererInfo.MaximumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + + int pointCount = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + pointCount += sri._series._seriesElements.Count; + + // Space shared by one clustered bar. + double groupWidth = cri.xAxisRendererInfo.MajorTick; + + // Space used by one bar. + double columnWidth = groupWidth * 0.75 / cri.seriesRendererInfos.Length; + + int seriesIdx = 0; + XPoint[] points = new XPoint[2]; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + // Set x to first clustered bar for each series. + double x = xMax - groupWidth / 2; + + // Offset for bars of a particular series from the start of a clustered bar. + double dx = (columnWidth * seriesIdx) - (columnWidth / 2 * cri.seriesRendererInfos.Length); + double y0 = yMin; + + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + if (column.Point != null) + { + double x0 = x - dx; + double x1 = x - dx - columnWidth; + double y1 = column.Point.Value; + + // Draw from zero base line, if it exists. + if (y0 < 0 && yMax >= 0) + y0 = 0; + + // y0 should always be lower than y1, i. e. draw bar from bottom to top. + if (y1 < 0 && y1 < y0) + { + double y = y0; + y0 = y1; + y1 = y; + } + + points[0].X = y0; // upper left + points[0].Y = x0; + points[1].X = y1; // lower right + points[1].Y = x1; + + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + column.Rect = new XRect(points[0].X, + points[1].Y, + points[1].X - points[0].X, + points[0].Y - points[1].Y); + } + x--; // Next clustered bar. + } + seriesIdx++; + } + } + + /// + /// If yValue is within the range from yMin to yMax returns true, otherwise false. + /// + protected override bool IsDataInside(double yMin, double yMax, double yValue) + { + return yValue <= yMax && yValue >= yMin; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarDataLabelRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarDataLabelRenderer.cs new file mode 100644 index 00000000..0ffad593 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarDataLabelRenderer.cs @@ -0,0 +1,162 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a data label renderer for bar charts. + /// + internal class BarDataLabelRenderer : DataLabelRenderer + { + /// + /// Initializes a new instance of the BarDataLabelRenderer class with the + /// specified renderer parameters. + /// + internal BarDataLabelRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used by the data labels. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + XGraphics gfx = _rendererParms.Graphics; + + sri._dataLabelRendererInfo.Entries = new DataLabelEntryRendererInfo[sri._pointRendererInfos.Length]; + int index = 0; + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + DataLabelEntryRendererInfo dleri = new DataLabelEntryRendererInfo(); + if (sri._dataLabelRendererInfo.Type != DataLabelType.None) + { + if (sri._dataLabelRendererInfo.Type == DataLabelType.Value) + dleri.Text = column.Point._value.ToString(sri._dataLabelRendererInfo.Format); + else if (sri._dataLabelRendererInfo.Type == DataLabelType.Percent) + throw new InvalidOperationException(PSCSR.PercentNotSupportedByColumnDataLabel); + + if (dleri.Text.Length > 0) + dleri.Size = gfx.MeasureString(dleri.Text, sri._dataLabelRendererInfo.Font); + } + + sri._dataLabelRendererInfo.Entries[index++] = dleri; + } + } + + CalcPositions(); + } + + /// + /// Draws the data labels of the bar chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + XGraphics gfx = _rendererParms.Graphics; + XFont font = sri._dataLabelRendererInfo.Font; + XBrush fontColor = sri._dataLabelRendererInfo.FontColor; + XStringFormat format = XStringFormats.Center; + format.LineAlignment = XLineAlignment.Center; + foreach (DataLabelEntryRendererInfo dataLabel in sri._dataLabelRendererInfo.Entries) + { + if (dataLabel.Text != null) + gfx.DrawString(dataLabel.Text, font, fontColor, dataLabel.Rect, format); + } + } + } + + /// + /// Calculates the data label positions specific for column charts. + /// + internal override void CalcPositions() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XGraphics gfx = _rendererParms.Graphics; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + int columnIndex = 0; + foreach (ColumnRendererInfo bar in sri._pointRendererInfos) + { + DataLabelEntryRendererInfo dleri = sri._dataLabelRendererInfo.Entries[columnIndex++]; + + dleri.Y = bar.Rect.Y + (bar.Rect.Height - dleri.Height) / 2; // Always the same... + switch (sri._dataLabelRendererInfo.Position) + { + case DataLabelPosition.InsideEnd: + // Inner border of the column. + dleri.X = bar.Rect.X; + if (bar.Point._value > 0) + dleri.X += bar.Rect.Width - dleri.Width; + break; + + case DataLabelPosition.Center: + // Centered inside the column. + dleri.X = bar.Rect.X + (bar.Rect.Width - dleri.Width) / 2; + break; + + case DataLabelPosition.InsideBase: + // Aligned at the base of the column. + dleri.X = bar.Rect.X; + if (bar.Point._value < 0) + dleri.X += bar.Rect.Width - dleri.Width; + break; + + case DataLabelPosition.OutsideEnd: + // Outer border of the column. + dleri.X = bar.Rect.X; + if (bar.Point._value > 0) + dleri.X += bar.Rect.Width; + else + dleri.X -= dleri.Width; + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarGridlinesRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarGridlinesRenderer.cs new file mode 100644 index 00000000..e925d2e7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarGridlinesRenderer.cs @@ -0,0 +1,135 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents gridlines used by bar charts, i. e. X axis grid will be rendered + /// from left to right and Y axis grid will be rendered from top to bottom of the plot area. + /// + internal class BarGridlinesRenderer : GridlinesRenderer + { + /// + /// Initializes a new instance of the BarGridlinesRenderer class with the + /// specified renderer parameters. + /// + internal BarGridlinesRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the gridlines into the plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; + if (plotAreaRect.IsEmpty) + return; + + AxisRendererInfo xari = cri.xAxisRendererInfo; + AxisRendererInfo yari = cri.yAxisRendererInfo; + + double xMin = xari.MinimumScale; + double xMax = xari.MaximumScale; + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double xMajorTick = xari.MajorTick; + double yMajorTick = yari.MajorTick; + double xMinorTick = xari.MinorTick; + double yMinorTick = yari.MinorTick; + double xMaxExtension = xari.MajorTick; + + XMatrix matrix = cri.plotAreaRendererInfo._matrix; + + LineFormatRenderer lineFormatRenderer; + XGraphics gfx = _rendererParms.Graphics; + + XPoint[] points = new XPoint[2]; + if (xari.MinorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, xari.MinorGridlinesLineFormat); + for (double x = xMin + xMinorTick; x < xMax; x += xMinorTick) + { + points[0].Y = x; + points[0].X = yMin; + points[1].Y = x; + points[1].X = yMax; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (xari.MajorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, xari.MajorGridlinesLineFormat); + for (double x = xMin; x <= xMax; x += xMajorTick) + { + points[0].Y = x; + points[0].X = yMin; + points[1].Y = x; + points[1].X = yMax; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (yari.MinorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, yari.MinorGridlinesLineFormat); + for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) + { + points[0].Y = xMin; + points[0].X = y; + points[1].Y = xMax; + points[1].X = y; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (yari.MajorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, yari.MajorGridlinesLineFormat); + for (double y = yMin; y <= yMax; y += yMajorTick) + { + points[0].Y = xMin; + points[0].X = y; + points[1].Y = xMax; + points[1].X = y; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarPlotAreaRenderer.cs new file mode 100644 index 00000000..a8758c37 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarPlotAreaRenderer.cs @@ -0,0 +1,154 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer for bars. + /// + internal abstract class BarPlotAreaRenderer : PlotAreaRenderer + { + /// + /// Initializes a new instance of the BarPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal BarPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Layouts and calculates the space for each bar. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double xMax = cri.xAxisRendererInfo.MaximumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + + XRect plotAreaBox = cri.plotAreaRendererInfo.Rect; + + cri.plotAreaRendererInfo._matrix = new XMatrix(); + cri.plotAreaRendererInfo._matrix.TranslatePrepend(-yMin, xMin); + cri.plotAreaRendererInfo._matrix.Scale(plotAreaBox.Width / (yMax - yMin), plotAreaBox.Height / (xMax - xMin), XMatrixOrder.Append); + cri.plotAreaRendererInfo._matrix.Translate(plotAreaBox.X, plotAreaBox.Y, XMatrixOrder.Append); + + CalcBars(); + } + + /// + /// Draws the content of the bar plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + XRect plotAreaBox = cri.plotAreaRendererInfo.Rect; + if (plotAreaBox.IsEmpty) + return; + + XGraphics gfx = _rendererParms.Graphics; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double xMax = cri.xAxisRendererInfo.MaximumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + + LineFormatRenderer lineFormatRenderer; + + // Under some circumstances it is possible that no zero base line will be drawn, + // e. g. because of unfavourable minimum/maximum scale and/or major tick, so force to draw + // a zero base line if necessary. + if (cri.yAxisRendererInfo.MajorGridlinesLineFormat != null || + cri.yAxisRendererInfo.MinorGridlinesLineFormat != null) + { + if (yMin < 0 && yMax > 0) + { + XPoint[] points = new XPoint[2]; + points[0].X = 0; + points[0].Y = xMin; + points[1].X = 0; + points[1].Y = xMax; + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + if (cri.yAxisRendererInfo.MinorGridlinesLineFormat != null) + lineFormatRenderer = new LineFormatRenderer(gfx, cri.yAxisRendererInfo.MinorGridlinesLineFormat); + else + lineFormatRenderer = new LineFormatRenderer(gfx, cri.yAxisRendererInfo.MajorGridlinesLineFormat); + + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Draw bars + XGraphicsState state = gfx.Save(); + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + // Do not draw bar if value is outside yMin/yMax range. Clipping does not make sense. + if (IsDataInside(yMin, yMax, column.Point._value)) + gfx.DrawRectangle(column.FillFormat, column.Rect); + } + } + + // Draw borders around bar. + // A border can overlap neighbor bars, so it is important to draw borders at the end. + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + // Do not draw bar if value is outside yMin/yMax range. Clipping does not make sense. + if (IsDataInside(yMin, yMax, column.Point._value)) + { + lineFormatRenderer = new LineFormatRenderer(gfx, column.LineFormat); + lineFormatRenderer.DrawRectangle(column.Rect); + } + } + } + gfx.Restore(state); + } + + /// + /// Calculates the position, width and height of each bar of all series. + /// + protected abstract void CalcBars(); + + /// + /// If yValue is within the range from yMin to yMax returns true, otherwise false. + /// + protected abstract bool IsDataInside(double yMin, double yMax, double yValue); + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarStackedPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarStackedPlotAreaRenderer.cs new file mode 100644 index 00000000..b8739090 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/BarStackedPlotAreaRenderer.cs @@ -0,0 +1,122 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of stacked bars, i. e. all bars are drawn one on another. + /// + internal class BarStackedPlotAreaRenderer : BarPlotAreaRenderer + { + /// + /// Initializes a new instance of the BarStackedPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal BarStackedPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the position, width and height of each bar of all series. + /// + protected override void CalcBars() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + double xMax = cri.xAxisRendererInfo.MaximumScale; + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + + int maxPoints = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + maxPoints = Math.Max(maxPoints, sri._series._seriesElements.Count); + + // Space used by one bar. + double x = xMax - xMajorTick / 2; + double columnWidth = xMajorTick * 0.75 / 2; + + XPoint[] points = new XPoint[2]; + for (int pointIdx = 0; pointIdx < maxPoints; ++pointIdx) + { + double yMin = 0, yMax = 0, y0 = 0, y1 = 0; + double x0 = x - columnWidth; + double x1 = x + columnWidth; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._pointRendererInfos.Length <= pointIdx) + break; + + ColumnRendererInfo column = (ColumnRendererInfo)sri._pointRendererInfos[pointIdx]; + if (column.Point != null && !double.IsNaN(column.Point._value)) + { + double y = column.Point._value; + if (y < 0) + { + y0 = yMin + y; + y1 = yMin; + yMin += y; + } + else + { + y0 = yMax; + y1 = yMax + y; + yMax += y; + } + + points[0].Y = x0; // oben links + points[0].X = y0; + points[1].Y = x1; // unten rechts + points[1].X = y1; + + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + column.Rect = new XRect(points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y); + } + } + x--; // Next stacked column. + } + } + + /// + /// If yValue is within the range from yMin to yMax returns true, otherwise false. + /// + protected override bool IsDataInside(double yMin, double yMax, double yValue) + { + return yValue <= yMax && yValue >= yMin; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ChartRenderer.cs new file mode 100644 index 00000000..028d3502 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ChartRenderer.cs @@ -0,0 +1,100 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base class for all chart renderers. + /// + internal abstract class ChartRenderer : Renderer + { + /// + /// Initializes a new instance of the ChartRenderer class with the specified renderer parameters. + /// + internal ChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used by the legend and returns the remaining space available for the + /// other parts of the chart. + /// + protected XRect LayoutLegend() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XRect remainingRect = _rendererParms.Box; + if (cri.legendRendererInfo != null) + { + switch (cri.legendRendererInfo._legend.Docking) + { + case DockingType.Left: + cri.legendRendererInfo.X = remainingRect.Left; + cri.legendRendererInfo.Y = remainingRect.Height / 2 - cri.legendRendererInfo.Height / 2; + double width = cri.legendRendererInfo.Width + LegendSpacing; + remainingRect.X += width; + remainingRect.Width -= width; + break; + + case DockingType.Right: + cri.legendRendererInfo.X = remainingRect.Right - cri.legendRendererInfo.Width; + cri.legendRendererInfo.Y = remainingRect.Height / 2 - cri.legendRendererInfo.Height / 2; + remainingRect.Width -= cri.legendRendererInfo.Width + LegendSpacing; + break; + + case DockingType.Top: + cri.legendRendererInfo.X = remainingRect.Width / 2 - cri.legendRendererInfo.Width / 2; + cri.legendRendererInfo.Y = remainingRect.Top; + double height = cri.legendRendererInfo.Height + LegendSpacing; + remainingRect.Y += height; + remainingRect.Height -= height; + break; + + case DockingType.Bottom: + cri.legendRendererInfo.X = remainingRect.Width / 2 - cri.legendRendererInfo.Width / 2; + cri.legendRendererInfo.Y = remainingRect.Bottom - cri.legendRendererInfo.Height; + remainingRect.Height -= cri.legendRendererInfo.Height + LegendSpacing; + break; + } + } + return remainingRect; + } + + /// + /// Used to separate the legend from the plot area. + /// + private const double LegendSpacing = 0; + + /// + /// Represents the default width for all series lines, like borders in column/bar charts. + /// + protected static readonly double DefaultSeriesLineWidth = 0.15; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Colors.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Colors.cs new file mode 100644 index 00000000..e47459b0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Colors.cs @@ -0,0 +1,126 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the predefined column/bar chart colors. + /// + internal sealed class ColumnColors + { + /// + /// Gets the color for column/bar charts from the specified index. + /// + public static XColor Item(int index) + { + return XColor.FromArgb((int)_seriesColors[index]); + } + + /// + /// Colors for column/bar charts taken from Excel. + /// + static readonly uint[] _seriesColors = new uint[] + { + 0xFF9999FF, 0xFF993366, 0xFFFFFFCC, 0xFFCCFFFF, 0xFF660066, 0xFFFF8080, + 0xFF0066CC, 0xFFCCCCFF, 0xFF000080, 0xFFFF00FF, 0xFFFFFF00, 0xFF00FFFF, + 0xFF800080, 0xFF800000, 0xFF008080, 0xFF0000FF, 0xFF00CCFF, 0xFFCCFFFF, + 0xFFCCFFCC, 0xFFFFFF99, 0xFF99CCFF, 0xFFFF99CC, 0xFFCC99FF, 0xFFFFCC99, + 0xFF3366FF, 0xFF33CCCC, 0xFF99CC00, 0xFFFFCC00, 0xFFFF9900, 0xFFFF6600, + 0xFF666699, 0xFF969696, 0xFF003366, 0xFF339966, 0xFF003300, 0xFF333300, + 0xFF993300, 0xFF993366, 0xFF333399, 0xFF333333, 0xFF000000, 0xFFFFFFFF, + 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, + 0xFF800000, 0xFF008000, 0xFF000080, 0xFF808000, 0xFF800080, 0xFF008080, + 0xFFC0C0C0, 0xFF808080 + }; + } + + /// + /// Represents the predefined line chart colors. + /// + internal sealed class LineColors + { + /// + /// Gets the color for line charts from the specified index. + /// + public static XColor Item(int index) + { + return XColor.FromArgb((int)_lineColors[index]); + } + + /// + /// Colors for line charts taken from Excel. + /// + static readonly uint[] _lineColors = new uint[] + { + 0xFF000080, 0xFFFF00FF, 0xFFFFFF00, 0xFF00FFFF, 0xFF800080, 0xFF800000, + 0xFF008080, 0xFF0000FF, 0xFF00CCFF, 0xFFCCFFFF, 0xFFCCFFCC, 0xFFFFFF99, + 0xFF99CCFF, 0xFFFF99CC, 0xFFCC99FF, 0xFFFFCC99, 0xFF3366FF, 0xFF33CCCC, + 0xFF99CC00, 0xFFFFCC00, 0xFFFF9900, 0xFFFF6600, 0xFF666699, 0xFF969696, + 0xFF003366, 0xFF339966, 0xFF003300, 0xFF333300, 0xFF993300, 0xFF993366, + 0xFF333399, 0xFF000000, 0xFFFFFFFF, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, + 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, 0xFF800000, 0xFF008000, 0xFF000080, + 0xFF808000, 0xFF800080, 0xFF008080, 0xFFC0C0C0, 0xFF808080, 0xFF9999FF, + 0xFF993366, 0xFFFFFFCC, 0xFFCCFFFF, 0xFF660066, 0xFFFF8080, 0xFF0066CC, + 0xFFCCCCFF + }; + } + + /// + /// Represents the predefined pie chart colors. + /// + internal sealed class PieColors + { + /// + /// Gets the color for pie charts from the specified index. + /// + public static XColor Item(int index) + { + return XColor.FromArgb((int)_sectorColors[index]); + } + + /// + /// Colors for pie charts taken from Excel. + /// + static readonly uint[] _sectorColors = new uint[] + { + 0xFF9999FF, 0xFF993366, 0xFFFFFFCC, 0xFFCCFFFF, 0xFF660066, 0xFFFF8080, + 0xFF0066CC, 0xFFCCCCFF, 0xFF000080, 0xFFFF00FF, 0xFFFFFF00, 0xFF00FFFF, + 0xFF800080, 0xFF800000, 0xFF008080, 0xFF0000FF, 0xFF00CCFF, 0xFFCCFFFF, + 0xFFCCFFCC, 0xFFFFFF99, 0xFF99CCFF, 0xFFFF99CC, 0xFFCC99FF, 0xFFFFCC99, + 0xFF3366FF, 0xFF33CCCC, 0xFF99CC00, 0xFFFFCC00, 0xFFFF9900, 0xFFFF6600, + 0xFF666699, 0xFF969696, 0xFF003366, 0xFF339966, 0xFF003300, 0xFF333300, + 0xFF993300, 0xFF993366, 0xFF333399, 0xFF333333, 0xFF000000, 0xFFFFFFFF, + 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF, + 0xFF800000, 0xFF008000, 0xFF000080, 0xFF808000, 0xFF800080, 0xFF008080, + 0xFFC0C0C0, 0xFF808080 + }; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnChartRenderer.cs new file mode 100644 index 00000000..c3acd34c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnChartRenderer.cs @@ -0,0 +1,229 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a column chart renderer. + /// + internal class ColumnChartRenderer : ColumnLikeChartRenderer + { + /// + /// Initializes a new instance of the ColumnChartRenderer class with the + /// specified renderer parameters. + /// + internal ColumnChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = new ChartRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeriesRendererInfo(); + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + cri.xAxisRendererInfo = (AxisRendererInfo)xar.Init(); + + AxisRenderer yar = GetYAxisRenderer(); + cri.yAxisRendererInfo = (AxisRendererInfo)yar.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)renderer.Init(); + + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Init(); + + return cri; + } + + /// + /// Layouts and calculates the space used by the column chart. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Format(); + + // axes + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Format(); + + AxisRenderer yar = GetYAxisRenderer(); + yar.Format(); + + // Calculate rects and positions. + CalcLayout(); + + // Calculated remaining plot area, now it's safe to format. + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Format(); + + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Format(); + } + + /// + /// Draws the column chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Draw(); + + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + GridlinesRenderer glr = new ColumnLikeGridlinesRenderer(_rendererParms); + glr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Draw(); + + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Draw(); + + if (cri.xAxisRendererInfo._axis != null) + { + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Draw(); + } + + if (cri.yAxisRendererInfo._axis != null) + { + AxisRenderer yar = GetYAxisRenderer(); + yar.Draw(); + } + } + + /// + /// Returns the specific plot area renderer. + /// + private PlotAreaRenderer GetPlotAreaRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Column2D: + return new ColumnClusteredPlotAreaRenderer(_rendererParms); + + case ChartType.ColumnStacked2D: + return new ColumnStackedPlotAreaRenderer(_rendererParms); + } + return null; + } + + /// + /// Returns the specific y axis renderer. + /// + private YAxisRenderer GetYAxisRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Column2D: + return new VerticalYAxisRenderer(_rendererParms); + + case ChartType.ColumnStacked2D: + return new VerticalStackedYAxisRenderer(_rendererParms); + } + return null; + } + + /// + /// Initializes all necessary data to draw all series for a column chart. + /// + private void InitSeriesRendererInfo() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + SeriesCollection seriesColl = cri._chart.SeriesCollection; + cri.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + sri._series = seriesColl[idx]; + cri.seriesRendererInfos[idx] = sri; + } + + InitSeries(); + } + + /// + /// Initializes all necessary data to draw all series for a column chart. + /// + internal void InitSeries() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int seriesIndex = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, XColors.Black, ChartRenderer.DefaultSeriesLineWidth); + sri.FillFormat = Converter.ToXBrush(sri._series._fillFormat, ColumnColors.Item(seriesIndex++)); + + sri._pointRendererInfos = new ColumnRendererInfo[sri._series._seriesElements.Count]; + for (int pointIdx = 0; pointIdx < sri._pointRendererInfos.Length; ++pointIdx) + { + PointRendererInfo pri = new ColumnRendererInfo(); + Point point = sri._series._seriesElements[pointIdx]; + pri.Point = point; + if (point != null) + { + pri.LineFormat = sri.LineFormat; + pri.FillFormat = sri.FillFormat; + if (point._lineFormat != null) + pri.LineFormat = Converter.ToXPen(point._lineFormat, sri.LineFormat); + if (point._fillFormat != null && !point._fillFormat._color.IsEmpty) + pri.FillFormat = new XSolidBrush(point._fillFormat._color); + } + sri._pointRendererInfos[pointIdx] = pri; + } + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnClusteredPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnClusteredPlotAreaRenderer.cs new file mode 100644 index 00000000..4c615203 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnClusteredPlotAreaRenderer.cs @@ -0,0 +1,127 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of clustered columns, i. e. all columns are drawn side by side. + /// + internal class ColumnClusteredPlotAreaRenderer : ColumnPlotAreaRenderer + { + /// + /// Initializes a new instance of the ColumnClusteredPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal ColumnClusteredPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the position, width and height of each column of all series. + /// + protected override void CalcColumns() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + + int pointCount = 0; + foreach (SeriesRendererInfo sr in cri.seriesRendererInfos) + pointCount += sr._series._seriesElements.Count; + + // Space shared by one clustered column. + double groupWidth = cri.xAxisRendererInfo.MajorTick; + + // Space used by one column. + double columnWidth = groupWidth * 3 / 4 / cri.seriesRendererInfos.Length; + + int seriesIdx = 0; + XPoint[] points = new XPoint[2]; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + // Set x to first clustered column for each series. + double x = xMin + groupWidth / 2; + + // Offset for columns of a particular series from the start of a clustered cloumn. + double dx = (columnWidth * seriesIdx) - (columnWidth / 2 * cri.seriesRendererInfos.Length); + + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + if (column.Point != null) + { + double x0 = x + dx; + double x1 = x + dx + columnWidth; + double y0 = yMin; + double y1 = column.Point.Value; + + // Draw from zero base line, if it exists. + if (y0 < 0 && yMax >= 0) + y0 = 0; + + // y0 should always be lower than y1, i. e. draw column from bottom to top. + if (y1 < 0 && y1 < y0) + { + double y = y0; + y0 = y1; + y1 = y; + } + + points[0].X = x0; // upper left + points[0].Y = y1; + points[1].X = x1; // lower right + points[1].Y = y0; + + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + column.Rect = new XRect(points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y); + } + x++; // Next clustered column. + } + seriesIdx++; + } + } + + /// + /// If yValue is within the range from yMin to yMax returns true, otherwise false. + /// + protected override bool IsDataInside(double yMin, double yMax, double yValue) + { + return yValue <= yMax && yValue >= yMin; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnDataLabelRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnDataLabelRenderer.cs new file mode 100644 index 00000000..0de184c9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnDataLabelRenderer.cs @@ -0,0 +1,160 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a data label renderer for column charts. + /// + internal class ColumnDataLabelRenderer : DataLabelRenderer + { + /// + /// Initializes a new instance of the ColumnDataLabelRenderer class with the + /// specified renderer parameters. + /// + internal ColumnDataLabelRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used by the data labels. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + XGraphics gfx = _rendererParms.Graphics; + + sri._dataLabelRendererInfo.Entries = new DataLabelEntryRendererInfo[sri._pointRendererInfos.Length]; + int index = 0; + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + DataLabelEntryRendererInfo dleri = new DataLabelEntryRendererInfo(); + if (sri._dataLabelRendererInfo.Type != DataLabelType.None) + { + if (sri._dataLabelRendererInfo.Type == DataLabelType.Value) + dleri.Text = column.Point._value.ToString(sri._dataLabelRendererInfo.Format); + else if (sri._dataLabelRendererInfo.Type == DataLabelType.Percent) + throw new InvalidOperationException(PSCSR.PercentNotSupportedByColumnDataLabel); + + if (dleri.Text.Length > 0) + dleri.Size = gfx.MeasureString(dleri.Text, sri._dataLabelRendererInfo.Font); + } + + sri._dataLabelRendererInfo.Entries[index++] = dleri; + } + } + + CalcPositions(); + } + + /// + /// Draws the data labels of the column chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + XGraphics gfx = _rendererParms.Graphics; + XFont font = sri._dataLabelRendererInfo.Font; + XBrush fontColor = sri._dataLabelRendererInfo.FontColor; + XStringFormat format = XStringFormats.Center; + format.LineAlignment = XLineAlignment.Center; + foreach (DataLabelEntryRendererInfo dataLabel in sri._dataLabelRendererInfo.Entries) + { + if (dataLabel.Text != null) + gfx.DrawString(dataLabel.Text, font, fontColor, dataLabel.Rect, format); + } + } + } + + /// + /// Calculates the data label positions specific for column charts. + /// + internal override void CalcPositions() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XGraphics gfx = _rendererParms.Graphics; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._dataLabelRendererInfo == null) + continue; + + int columnIndex = 0; + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + DataLabelEntryRendererInfo dleri = sri._dataLabelRendererInfo.Entries[columnIndex++]; + + dleri.X = column.Rect.X + column.Rect.Width / 2 - dleri.Width / 2; // Always the same... + switch (sri._dataLabelRendererInfo.Position) + { + case DataLabelPosition.InsideEnd: + // Inner border of the column. + dleri.Y = column.Rect.Y; + if (column.Point._value < 0) + dleri.Y = column.Rect.Y + column.Rect.Height - dleri.Height; + break; + + case DataLabelPosition.Center: + // Centered inside the column. + dleri.Y = column.Rect.Y + column.Rect.Height / 2 - dleri.Height / 2; + break; + + case DataLabelPosition.InsideBase: + // Aligned at the base of the column. + dleri.Y = column.Rect.Y + column.Rect.Height - dleri.Height; + if (column.Point._value < 0) + dleri.Y = column.Rect.Y; + break; + + case DataLabelPosition.OutsideEnd: + // Outer border of the column. + dleri.Y = column.Rect.Y - dleri.Height; + if (column.Point._value < 0) + dleri.Y = column.Rect.Y + column.Rect.Height; + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeChartRenderer.cs new file mode 100644 index 00000000..cd3ba567 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeChartRenderer.cs @@ -0,0 +1,68 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents column like chart renderer. + /// + internal abstract class ColumnLikeChartRenderer : ChartRenderer + { + /// + /// Initializes a new instance of the ColumnLikeChartRenderer class with the + /// specified renderer parameters. + /// + internal ColumnLikeChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the chart layout. + /// + internal void CalcLayout() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + // Calculate rects and positions. + XRect chartRect = LayoutLegend(); + cri.xAxisRendererInfo.X = chartRect.Left + cri.yAxisRendererInfo.Width; + cri.xAxisRendererInfo.Y = chartRect.Bottom - cri.xAxisRendererInfo.Height; + cri.xAxisRendererInfo.Width = chartRect.Width - cri.yAxisRendererInfo.Width; + cri.yAxisRendererInfo.X = chartRect.Left; + cri.yAxisRendererInfo.Y = chartRect.Top; + cri.yAxisRendererInfo.Height = cri.xAxisRendererInfo.Y - chartRect.Top; + cri.plotAreaRendererInfo.X = cri.xAxisRendererInfo.X; + cri.plotAreaRendererInfo.Y = cri.yAxisRendererInfo.InnerRect.Y; + cri.plotAreaRendererInfo.Width = cri.xAxisRendererInfo.Width; + cri.plotAreaRendererInfo.Height = cri.yAxisRendererInfo.InnerRect.Height; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeGridlinesRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeGridlinesRenderer.cs new file mode 100644 index 00000000..9ea91585 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeGridlinesRenderer.cs @@ -0,0 +1,134 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents gridlines used by column or line charts, i. e. X axis grid will be rendered + /// from top to bottom and Y axis grid will be rendered from left to right of the plot area. + /// + internal class ColumnLikeGridlinesRenderer : GridlinesRenderer + { + /// + /// Initializes a new instance of the ColumnLikeGridlinesRenderer class with the + /// specified renderer parameters. + /// + internal ColumnLikeGridlinesRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the gridlines into the plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; + if (plotAreaRect.IsEmpty) + return; + + AxisRendererInfo xari = cri.xAxisRendererInfo; + AxisRendererInfo yari = cri.yAxisRendererInfo; + + double xMin = xari.MinimumScale; + double xMax = xari.MaximumScale; + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double xMajorTick = xari.MajorTick; + double yMajorTick = yari.MajorTick; + double xMinorTick = xari.MinorTick; + double yMinorTick = yari.MinorTick; + + XMatrix matrix = cri.plotAreaRendererInfo._matrix; + + LineFormatRenderer lineFormatRenderer; + XGraphics gfx = _rendererParms.Graphics; + + XPoint[] points = new XPoint[2]; + if (xari.MinorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, xari.MinorGridlinesLineFormat); + for (double x = xMin + xMinorTick; x < xMax; x += xMinorTick) + { + points[0].X = x; + points[0].Y = yMin; + points[1].X = x; + points[1].Y = yMax; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (xari.MajorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, xari.MajorGridlinesLineFormat); + for (double x = xMin; x <= xMax; x += xMajorTick) + { + points[0].X = x; + points[0].Y = yMin; + points[1].X = x; + points[1].Y = yMax; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (yari.MinorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, yari.MinorGridlinesLineFormat); + for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) + { + points[0].X = xMin; + points[0].Y = y; + points[1].X = xMax; + points[1].Y = y; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + if (yari.MajorGridlinesLineFormat != null) + { + lineFormatRenderer = new LineFormatRenderer(gfx, yari.MajorGridlinesLineFormat); + for (double y = yMin; y <= yMax; y += yMajorTick) + { + points[0].X = xMin; + points[0].Y = y; + points[1].X = xMax; + points[1].Y = y; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeLegendRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeLegendRenderer.cs new file mode 100644 index 00000000..f4bf5e94 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikeLegendRenderer.cs @@ -0,0 +1,96 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the legend renderer specific to charts like column, line, or bar. + /// + internal class ColumnLikeLegendRenderer : LegendRenderer + { + /// + /// Initializes a new instance of the ColumnLikeLegendRenderer class with the + /// specified renderer parameters. + /// + internal ColumnLikeLegendRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Initializes the legend's renderer info. Each data series will be represented through + /// a legend entry renderer info. + /// + internal override RendererInfo Init() + { + LegendRendererInfo lri = null; + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri._chart._legend != null) + { + lri = new LegendRendererInfo(); + lri._legend = cri._chart._legend; + + lri.Font = Converter.ToXFont(lri._legend._font, cri.DefaultFont); + lri.FontColor = new XSolidBrush(XColors.Black); + + if (lri._legend._lineFormat != null) + lri.BorderPen = Converter.ToXPen(lri._legend._lineFormat, XColors.Black, DefaultLineWidth, XDashStyle.Solid); + + lri.Entries = new LegendEntryRendererInfo[cri.seriesRendererInfos.Length]; + int index = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + LegendEntryRendererInfo leri = new LegendEntryRendererInfo(); + leri._seriesRendererInfo = sri; + leri._legendRendererInfo = lri; + leri.EntryText = sri._series.Name; + if (sri._markerRendererInfo != null) + { + leri.MarkerSize.Width = leri.MarkerSize.Height = sri._markerRendererInfo.MarkerSize.Point; + leri.MarkerPen = new XPen(sri._markerRendererInfo.MarkerForegroundColor); + leri.MarkerBrush = new XSolidBrush(sri._markerRendererInfo.MarkerBackgroundColor); + } + else + { + leri.MarkerPen = sri.LineFormat; + leri.MarkerBrush = sri.FillFormat; + } + + if (cri._chart._type == ChartType.ColumnStacked2D) + // Stacked columns are in reverse order. + lri.Entries[cri.seriesRendererInfos.Length - index++ - 1] = leri; + else + lri.Entries[index++] = leri; + } + } + return lri; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikePlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikePlotAreaRenderer.cs new file mode 100644 index 00000000..fc8de095 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnLikePlotAreaRenderer.cs @@ -0,0 +1,68 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Base class for all plot area renderers. + /// + internal abstract class ColumnLikePlotAreaRenderer : PlotAreaRenderer + { + /// + /// Initializes a new instance of the ColumnLikePlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal ColumnLikePlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Layouts and calculates the space for column like plot areas. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double xMax = cri.xAxisRendererInfo.MaximumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + + XRect plotAreaBox = cri.plotAreaRendererInfo.Rect; + + cri.plotAreaRendererInfo._matrix = new XMatrix(); + cri.plotAreaRendererInfo._matrix.TranslatePrepend(-xMin, yMax); + cri.plotAreaRendererInfo._matrix.Scale(plotAreaBox.Width / xMax, plotAreaBox.Height / (yMax - yMin), XMatrixOrder.Append); + cri.plotAreaRendererInfo._matrix.ScalePrepend(1, -1); + cri.plotAreaRendererInfo._matrix.Translate(plotAreaBox.X, plotAreaBox.Y, XMatrixOrder.Append); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnPlotAreaRenderer.cs new file mode 100644 index 00000000..3876b5ee --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnPlotAreaRenderer.cs @@ -0,0 +1,139 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of clustered columns, i. e. all columns are drawn side by side. + /// + internal abstract class ColumnPlotAreaRenderer : ColumnLikePlotAreaRenderer + { + /// + /// Initializes a new instance of the ColumnPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal ColumnPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Layouts and calculates the space for each column. + /// + internal override void Format() + { + base.Format(); + CalcColumns(); + } + + /// + /// Draws the content of the column plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + XRect plotAreaBox = cri.plotAreaRendererInfo.Rect; + if (plotAreaBox.IsEmpty) + return; + + XGraphics gfx = _rendererParms.Graphics; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double xMax = cri.xAxisRendererInfo.MaximumScale; + double yMin = cri.yAxisRendererInfo.MinimumScale; + double yMax = cri.yAxisRendererInfo.MaximumScale; + + LineFormatRenderer lineFormatRenderer; + + // Under some circumstances it is possible that no zero base line will be drawn, + // e. g. because of unfavourable minimum/maximum scale and/or major tick, so force to draw + // a zero base line if necessary. + if (cri.yAxisRendererInfo.MajorGridlinesLineFormat != null || + cri.yAxisRendererInfo.MinorGridlinesLineFormat != null) + { + if (yMin < 0 && yMax > 0) + { + XPoint[] points = new XPoint[2]; + points[0].X = xMin; + points[0].Y = 0; + points[1].X = xMax; + points[1].Y = 0; + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + if (cri.yAxisRendererInfo.MinorGridlinesLineFormat != null) + lineFormatRenderer = new LineFormatRenderer(gfx, cri.yAxisRendererInfo.MinorGridlinesLineFormat); + else + lineFormatRenderer = new LineFormatRenderer(gfx, cri.yAxisRendererInfo.MajorGridlinesLineFormat); + + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Draw columns + XGraphicsState state = gfx.Save(); + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + // Do not draw column if value is outside yMin/yMax range. Clipping does not make sense. + if (IsDataInside(yMin, yMax, column.Point._value)) + gfx.DrawRectangle(column.FillFormat, column.Rect); + } + } + + // Draw borders around column. + // A border can overlap neighbor columns, so it is important to draw borders at the end. + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + foreach (ColumnRendererInfo column in sri._pointRendererInfos) + { + // Do not draw column if value is outside yMin/yMax range. Clipping does not make sense. + if (IsDataInside(yMin, yMax, column.Point._value) && column.LineFormat.Width > 0) + { + lineFormatRenderer = new LineFormatRenderer(gfx, column.LineFormat); + lineFormatRenderer.DrawRectangle(column.Rect); + } + } + } + gfx.Restore(state); + } + + /// + /// Calculates the position, width and height of each column of all series. + /// + protected abstract void CalcColumns(); + + /// + /// If yValue is within the range from yMin to yMax returns true, otherwise false. + /// + protected abstract bool IsDataInside(double yMin, double yMax, double yValue); + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnStackedPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnStackedPlotAreaRenderer.cs new file mode 100644 index 00000000..ef5dc3a5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/ColumnStackedPlotAreaRenderer.cs @@ -0,0 +1,124 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a plot area renderer of stacked columns, i. e. all columns are drawn one on another. + /// + internal class ColumnStackedPlotAreaRenderer : ColumnPlotAreaRenderer + { + /// + /// Initializes a new instance of the ColumnStackedPlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal ColumnStackedPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the position, width and height of each column of all series. + /// + protected override void CalcColumns() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + double xMin = cri.xAxisRendererInfo.MinimumScale; + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + + int maxPoints = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + maxPoints = Math.Max(maxPoints, sri._series._seriesElements.Count); + + double x = xMin + xMajorTick / 2; + + // Space used by one column. + double columnWidth = xMajorTick * 0.75 / 2; + + XPoint[] points = new XPoint[2]; + for (int pointIdx = 0; pointIdx < maxPoints; ++pointIdx) + { + // Set x to first clustered column for each series. + double yMin = 0, yMax = 0, y0 = 0, y1 = 0; + double x0 = x - columnWidth; + double x1 = x + columnWidth; + + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._pointRendererInfos.Length <= pointIdx) + break; + + ColumnRendererInfo column = (ColumnRendererInfo)sri._pointRendererInfos[pointIdx]; + if (column.Point != null && !double.IsNaN(column.Point._value)) + { + double y = column.Point._value; + if (y < 0) + { + y0 = yMin + y; + y1 = yMin; + yMin += y; + } + else + { + y0 = yMax; + y1 = yMax + y; + yMax += y; + } + + points[0].X = x0; // upper left + points[0].Y = y1; + points[1].X = x1; // lower right + points[1].Y = y0; + + cri.plotAreaRendererInfo._matrix.TransformPoints(points); + + column.Rect = new XRect(points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y); + } + } + x++; // Next stacked column. + } + } + + /// + /// Stacked columns are always inside. + /// + protected override bool IsDataInside(double yMin, double yMax, double yValue) + { + return true; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/CombinationChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/CombinationChartRenderer.cs new file mode 100644 index 00000000..cf2ee8a6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/CombinationChartRenderer.cs @@ -0,0 +1,295 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a renderer for combinations of charts. + /// + internal class CombinationChartRenderer : ChartRenderer + { + /// + /// Initializes a new instance of the CombinationChartRenderer class with the + /// specified renderer parameters. + /// + internal CombinationChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + CombinationRendererInfo cri = new CombinationRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeriesRendererInfo(); + DistributeSeries(); + + if (cri._areaSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._areaSeriesRendererInfos; + AreaChartRenderer renderer = new AreaChartRenderer(_rendererParms); + renderer.InitSeries(); + } + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + ColumnChartRenderer renderer = new ColumnChartRenderer(_rendererParms); + renderer.InitSeries(); + } + if (cri._lineSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._lineSeriesRendererInfos; + LineChartRenderer renderer = new LineChartRenderer(_rendererParms); + renderer.InitSeries(); + } + cri.seriesRendererInfos = cri._commonSeriesRendererInfos; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + cri.xAxisRendererInfo = (AxisRendererInfo)xar.Init(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + cri.yAxisRendererInfo = (AxisRendererInfo)yar.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + PlotAreaRenderer apar = new AreaPlotAreaRenderer(_rendererParms); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)apar.Init(); + + // Draw data labels. + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Init(); + } + + return cri; + } + + /// + /// Layouts and calculates the space used by the combination chart. + /// + internal override void Format() + { + CombinationRendererInfo cri = (CombinationRendererInfo)_rendererParms.RendererInfo; + cri.seriesRendererInfos = cri._commonSeriesRendererInfos; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Format(); + + // axes + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Format(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Format(); + + // Calculate rects and positions. + XRect chartRect = LayoutLegend(); + cri.xAxisRendererInfo.X = chartRect.Left + cri.yAxisRendererInfo.Width; + cri.xAxisRendererInfo.Y = chartRect.Bottom - cri.xAxisRendererInfo.Height; + cri.xAxisRendererInfo.Width = chartRect.Width - cri.yAxisRendererInfo.Width; + cri.yAxisRendererInfo.X = chartRect.Left; + cri.yAxisRendererInfo.Y = chartRect.Top; + cri.yAxisRendererInfo.Height = chartRect.Height - cri.xAxisRendererInfo.Height; + cri.plotAreaRendererInfo.X = cri.xAxisRendererInfo.X; + cri.plotAreaRendererInfo.Y = cri.yAxisRendererInfo.InnerRect.Y; + cri.plotAreaRendererInfo.Width = cri.xAxisRendererInfo.Width; + cri.plotAreaRendererInfo.Height = cri.yAxisRendererInfo.InnerRect.Height; + + // Calculated remaining plot area, now it's safe to format. + PlotAreaRenderer renderer; + if (cri._areaSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._areaSeriesRendererInfos; + renderer = new AreaPlotAreaRenderer(_rendererParms); + renderer.Format(); + } + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + //TODO Check for Clustered- or StackedPlotAreaRenderer + renderer = new ColumnClusteredPlotAreaRenderer(_rendererParms); + renderer.Format(); + } + if (cri._lineSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._lineSeriesRendererInfos; + renderer = new LinePlotAreaRenderer(_rendererParms); + renderer.Format(); + } + + // Draw data labels. + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Format(); + } + } + + /// + /// Draws the column chart. + /// + internal override void Draw() + { + CombinationRendererInfo cri = (CombinationRendererInfo)_rendererParms.RendererInfo; + cri.seriesRendererInfos = cri._commonSeriesRendererInfos; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Draw(); + + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + GridlinesRenderer glr = new ColumnLikeGridlinesRenderer(_rendererParms); + glr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + PlotAreaRenderer renderer; + if (cri._areaSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._areaSeriesRendererInfos; + renderer = new AreaPlotAreaRenderer(_rendererParms); + renderer.Draw(); + } + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + //TODO Check for Clustered- or StackedPlotAreaRenderer + renderer = new ColumnClusteredPlotAreaRenderer(_rendererParms); + renderer.Draw(); + } + if (cri._lineSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._lineSeriesRendererInfos; + renderer = new LinePlotAreaRenderer(_rendererParms); + renderer.Draw(); + } + + // Draw data labels. + if (cri._columnSeriesRendererInfos != null) + { + cri.seriesRendererInfos = cri._columnSeriesRendererInfos; + DataLabelRenderer dlr = new ColumnDataLabelRenderer(_rendererParms); + dlr.Draw(); + } + + // Draw axes. + cri.seriesRendererInfos = cri._commonSeriesRendererInfos; + if (cri.xAxisRendererInfo._axis != null) + { + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Draw(); + } + if (cri.yAxisRendererInfo._axis != null) + { + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Draw(); + } + } + + /// + /// Initializes all necessary data to draw series for a combination chart. + /// + private void InitSeriesRendererInfo() + { + CombinationRendererInfo cri = (CombinationRendererInfo)_rendererParms.RendererInfo; + SeriesCollection seriesColl = cri._chart.SeriesCollection; + cri.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + sri._series = seriesColl[idx]; + cri.seriesRendererInfos[idx] = sri; + } + } + + /// + /// Sort all series renderer info dependent on their chart type. + /// + private void DistributeSeries() + { + CombinationRendererInfo cri = (CombinationRendererInfo)_rendererParms.RendererInfo; + + List areaSeries = new List(); + List columnSeries = new List(); + List lineSeries = new List(); + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + switch (sri._series._chartType) + { + case ChartType.Area2D: + areaSeries.Add(sri); + break; + + case ChartType.Column2D: + columnSeries.Add(sri); + break; + + case ChartType.Line: + lineSeries.Add(sri); + break; + + default: + throw new InvalidOperationException(PSCSR.InvalidChartTypeForCombination(sri._series._chartType)); + } + } + + cri._commonSeriesRendererInfos = cri.seriesRendererInfos; + if (areaSeries.Count > 0) + { + cri._areaSeriesRendererInfos = new SeriesRendererInfo[areaSeries.Count]; + areaSeries.CopyTo(cri._areaSeriesRendererInfos); + } + if (columnSeries.Count > 0) + { + cri._columnSeriesRendererInfos = new SeriesRendererInfo[columnSeries.Count]; + columnSeries.CopyTo(cri._columnSeriesRendererInfos); + } + if (lineSeries.Count > 0) + { + cri._lineSeriesRendererInfos = new SeriesRendererInfo[lineSeries.Count]; + lineSeries.CopyTo(cri._lineSeriesRendererInfos); + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Converter.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Converter.cs new file mode 100644 index 00000000..5b517689 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Converter.cs @@ -0,0 +1,138 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Provides functions which converts Charting.DOM objects into PdfSharp.Drawing objects. + /// + internal class Converter + { + /// + /// Creates a XFont based on the font. Missing attributes will be taken from the defaultFont + /// parameter. + /// + internal static XFont ToXFont(Font font, XFont defaultFont) + { + XFont xfont = defaultFont; + if (font != null) + { + string fontFamily = font.Name; + if (fontFamily == "") + fontFamily = defaultFont.FontFamily.Name; + + XFontStyle fontStyle = defaultFont.Style; + if (font._bold) + fontStyle |= XFontStyle.Bold; + if (font._italic) + fontStyle |= XFontStyle.Italic; + + double size = font._size.Point; //emSize??? + if (size == 0) + size = defaultFont.Size; + + xfont = new XFont(fontFamily, size, fontStyle); + } + return xfont; + } + + /// + /// Creates a XPen based on the specified line format. If not specified color and width will be taken + /// from the defaultColor and defaultWidth parameter. + /// + internal static XPen ToXPen(LineFormat lineFormat, XColor defaultColor, double defaultWidth) + { + return ToXPen(lineFormat, defaultColor, defaultWidth, XDashStyle.Solid); + } + + /// + /// Creates a XPen based on the specified line format. If not specified color and width will be taken + /// from the defaultPen parameter. + /// + internal static XPen ToXPen(LineFormat lineFormat, XPen defaultPen) + { + return ToXPen(lineFormat, defaultPen.Color, defaultPen.Width, defaultPen.DashStyle); + } + + /// + /// Creates a XPen based on the specified line format. If not specified color, width and dash style + /// will be taken from the defaultColor, defaultWidth and defaultDashStyle parameters. + /// + internal static XPen ToXPen(LineFormat lineFormat, XColor defaultColor, double defaultWidth, XDashStyle defaultDashStyle) + { + XPen pen = null; + if (lineFormat == null) + { + pen = new XPen(defaultColor, defaultWidth); + pen.DashStyle = defaultDashStyle; + } + else + { + XColor color = defaultColor; + if (!lineFormat.Color.IsEmpty) + color = lineFormat.Color; + + double width = lineFormat.Width.Point; + if (!lineFormat.Visible) + width = 0; + if (lineFormat.Visible && width == 0) + width = defaultWidth; + + pen = new XPen(color, width); + pen.DashStyle = lineFormat._dashStyle; + pen.DashOffset = 10 * width; + } + return pen; + } + + /// + /// Creates a XBrush based on the specified fill format. If not specified, color will be taken + /// from the defaultColor parameter. + /// + internal static XBrush ToXBrush(FillFormat fillFormat, XColor defaultColor) + { + if (fillFormat == null || fillFormat._color.IsEmpty) + return new XSolidBrush(defaultColor); + return new XSolidBrush(fillFormat._color); + } + + /// + /// Creates a XBrush based on the specified font color. If not specified, color will be taken + /// from the defaultColor parameter. + /// + internal static XBrush ToXBrush(Font font, XColor defaultColor) + { + if (font == null || font._color.IsEmpty) + return new XSolidBrush(defaultColor); + return new XSolidBrush(font._color); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/DataLabelRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/DataLabelRenderer.cs new file mode 100644 index 00000000..fe9a56c6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/DataLabelRenderer.cs @@ -0,0 +1,107 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a data label renderer. + /// + internal abstract class DataLabelRenderer : Renderer + { + /// + /// Initializes a new instance of the DataLabelRenderer class with the + /// specified renderer parameters. + /// + internal DataLabelRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Creates a data label rendererInfo. + /// Does not return any renderer info. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (cri._chart._hasDataLabel || cri._chart._dataLabel != null || + sri._series._hasDataLabel || sri._series._dataLabel != null) + { + DataLabelRendererInfo dlri = new DataLabelRendererInfo(); + + DataLabel dl = sri._series._dataLabel; + if (dl == null) + dl = cri._chart._dataLabel; + if (dl == null) + { + dlri.Format = "0"; + dlri.Font = cri.DefaultDataLabelFont; + dlri.FontColor = new XSolidBrush(XColors.Black); + dlri.Position = DataLabelPosition.InsideEnd; + if (cri._chart._type == ChartType.Pie2D || cri._chart._type == ChartType.PieExploded2D) + dlri.Type = DataLabelType.Percent; + else + dlri.Type = DataLabelType.Value; + } + else + { + dlri.Format = dl.Format.Length > 0 ? dl.Format : "0"; + dlri.Font = Converter.ToXFont(dl._font, cri.DefaultDataLabelFont); + dlri.FontColor = Converter.ToXBrush(dl._font, XColors.Black); + if (dl._positionInitialized) + dlri.Position = dl._position; + else + dlri.Position = DataLabelPosition.OutsideEnd; + if (dl._typeInitialized) + dlri.Type = dl._type; + else + { + if (cri._chart._type == ChartType.Pie2D || cri._chart._type == ChartType.PieExploded2D) + dlri.Type = DataLabelType.Percent; + else + dlri.Type = DataLabelType.Value; + } + } + + sri._dataLabelRendererInfo = dlri; + } + } + + return null; + } + + /// + /// Calculates the specific positions for each data label. + /// + internal abstract void CalcPositions(); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/GridlinesRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/GridlinesRenderer.cs new file mode 100644 index 00000000..0761c07d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/GridlinesRenderer.cs @@ -0,0 +1,44 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Base class for all renderers used to draw gridlines. + /// + internal abstract class GridlinesRenderer : Renderer + { + /// + /// Initializes a new instance of the GridlinesRenderer class with the specified renderer parameters. + /// + internal GridlinesRenderer(RendererParameters parms) + : base(parms) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalStackedYAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalStackedYAxisRenderer.cs new file mode 100644 index 00000000..e54d4d30 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalStackedYAxisRenderer.cs @@ -0,0 +1,84 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a Y axis renderer used for charts of type BarStacked2D. + /// + internal class HorizontalStackedYAxisRenderer : HorizontalYAxisRenderer + { + /// + /// Initializes a new instance of the HorizontalStackedYAxisRenderer class with the + /// specified renderer parameters. + /// + internal HorizontalStackedYAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Determines the sum of the smallest and the largest stacked bar + /// from all series of the chart. + /// + protected override void CalcYAxis(out double yMin, out double yMax) + { + yMin = double.MaxValue; + yMax = double.MinValue; + + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int maxPoints = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + maxPoints = Math.Max(maxPoints, sri._series._seriesElements.Count); + + for (int pointIdx = 0; pointIdx < maxPoints; ++pointIdx) + { + double valueSumPos = 0, valueSumNeg = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._pointRendererInfos.Length <= pointIdx) + break; + + ColumnRendererInfo column = (ColumnRendererInfo)sri._pointRendererInfos[pointIdx]; + if (column.Point != null && !double.IsNaN(column.Point._value)) + { + if (column.Point._value < 0) + valueSumNeg += column.Point._value; + else + valueSumPos += column.Point._value; + } + } + yMin = Math.Min(valueSumNeg, yMin); + yMax = Math.Max(valueSumPos, yMax); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalXAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalXAxisRenderer.cs new file mode 100644 index 00000000..ea8b4c95 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalXAxisRenderer.cs @@ -0,0 +1,314 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents an axis renderer used for charts of type Column2D or Line. + /// + internal class HorizontalXAxisRenderer : XAxisRenderer + { + /// + /// Initializes a new instance of the HorizontalXAxisRenderer class with the specified renderer parameters. + /// + internal HorizontalXAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized rendererInfo based on the X axis. + /// + internal override RendererInfo Init() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + + AxisRendererInfo xari = new AxisRendererInfo(); + xari._axis = chart._xAxis; + if (xari._axis != null) + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + CalculateXAxisValues(xari); + InitTickLabels(xari, cri.DefaultFont); + InitXValues(xari); + InitAxisTitle(xari, cri.DefaultFont); + InitAxisLineFormat(xari); + InitGridlines(xari); + } + return xari; + } + + /// + /// Calculates the space used for the X axis. + /// + internal override void Format() + { + AxisRendererInfo xari = ((ChartRendererInfo)_rendererParms.RendererInfo).xAxisRendererInfo; + if (xari._axis != null) + { + AxisTitleRendererInfo atri = xari._axisTitleRendererInfo; + + // Calculate space used for axis title. + XSize titleSize = new XSize(0, 0); + if (atri != null && atri.AxisTitleText != null && atri.AxisTitleText.Length > 0) + { + titleSize = _rendererParms.Graphics.MeasureString(atri.AxisTitleText, atri.AxisTitleFont); + atri.AxisTitleSize = titleSize; + } + + // Calculate space used for tick labels. + XSize size = new XSize(0, 0); + if (xari.XValues.Count > 0) + { + XSeries xs = xari.XValues[0]; + foreach (XValue xv in xs) + { + if (xv != null) + { + string tickLabel = xv._value; + XSize valueSize = _rendererParms.Graphics.MeasureString(tickLabel, xari.TickLabelsFont); + size.Height = Math.Max(valueSize.Height, size.Height); + size.Width += valueSize.Width; + } + } + } + + // Remember space for later drawing. + xari.TickLabelsHeight = size.Height; + xari.Height = titleSize.Height + size.Height + xari.MajorTickMarkWidth; + xari.Width = Math.Max(titleSize.Width, size.Width); + } + } + + /// + /// Draws the horizontal X axis. + /// + internal override void Draw() + { + XGraphics gfx = _rendererParms.Graphics; + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + AxisRendererInfo xari = cri.xAxisRendererInfo; + + double xMin = xari.MinimumScale; + double xMax = xari.MaximumScale; + double xMajorTick = xari.MajorTick; + double xMinorTick = xari.MinorTick; + double xMaxExtension = xari.MajorTick; + + // Draw tick labels. Each tick label will be aligned centered. + int countTickLabels = (int)xMax; + double tickLabelStep = xari.Width; + if (countTickLabels != 0) + tickLabelStep = xari.Width / countTickLabels; + + //XPoint startPos = new XPoint(xari.X + tickLabelStep / 2, xari.Y + /*xari.TickLabelsHeight +*/ xari.MajorTickMarkWidth); + XPoint startPos = new XPoint(xari.X + tickLabelStep / 2, xari.Y + xari.TickLabelsHeight); + if (xari.MajorTickMark != TickMarkType.None) + startPos.Y += xari.MajorTickMarkWidth; + foreach (XSeries xs in xari.XValues) + { + for (int idx = 0; idx < countTickLabels && idx < xs.Count; ++idx) + { + XValue xv = xs[idx]; + if (xv != null) + { + string tickLabel = xv._value; + XSize size = gfx.MeasureString(tickLabel, xari.TickLabelsFont); + gfx.DrawString(tickLabel, xari.TickLabelsFont, xari.TickLabelsBrush, startPos.X - size.Width / 2, startPos.Y); + } + startPos.X += tickLabelStep; + } + } + + // Draw axis. + // First draw tick marks, second draw axis. + double majorTickMarkStart = 0, majorTickMarkEnd = 0, + minorTickMarkStart = 0, minorTickMarkEnd = 0; + GetTickMarkPos(xari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); + + LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, xari.LineFormat); + XPoint[] points = new XPoint[2]; + + // Minor ticks. + if (xari.MinorTickMark != TickMarkType.None) + { + int countMinorTickMarks = (int)(xMax / xMinorTick); + double minorTickMarkStep = xari.Width / countMinorTickMarks; + startPos.X = xari.X; + for (int x = 0; x <= countMinorTickMarks; x++) + { + points[0].X = startPos.X + minorTickMarkStep * x; + points[0].Y = minorTickMarkStart; + points[1].X = points[0].X; + points[1].Y = minorTickMarkEnd; + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Major ticks. + if (xari.MajorTickMark != TickMarkType.None) + { + int countMajorTickMarks = (int)(xMax / xMajorTick); + double majorTickMarkStep = xari.Width; + if (countMajorTickMarks != 0) + majorTickMarkStep = xari.Width / countMajorTickMarks; + startPos.X = xari.X; + for (int x = 0; x <= countMajorTickMarks; x++) + { + points[0].X = startPos.X + majorTickMarkStep * x; + points[0].Y = majorTickMarkStart; + points[1].X = points[0].X; + points[1].Y = majorTickMarkEnd; + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Axis. + if (xari.LineFormat != null) + { + points[0].X = xari.X; + points[0].Y = xari.Y; + points[1].X = xari.X + xari.Width; + points[1].Y = xari.Y; + if (xari.MajorTickMark != TickMarkType.None) + { + points[0].X -= xari.LineFormat.Width / 2; + points[1].X += xari.LineFormat.Width / 2; + } + lineFormatRenderer.DrawLine(points[0], points[1]); + } + + // Draw axis title. + AxisTitleRendererInfo atri = xari._axisTitleRendererInfo; + if (atri != null && atri.AxisTitleText != null && atri.AxisTitleText.Length > 0) + { + XRect rect = new XRect(xari.Rect.Right / 2 - atri.AxisTitleSize.Width / 2, xari.Rect.Bottom, + atri.AxisTitleSize.Width, 0); + gfx.DrawString(atri.AxisTitleText, atri.AxisTitleFont, atri.AxisTitleBrush, rect); + } + } + + /// + /// Calculates the X axis describing values like minimum/maximum scale, major/minor tick and + /// major/minor tick mark width. + /// + private void CalculateXAxisValues(AxisRendererInfo rendererInfo) + { + // Calculates the maximum number of data points over all series. + SeriesCollection seriesCollection = ((Chart)rendererInfo._axis._parent)._seriesCollection; + int count = 0; + foreach (Series series in seriesCollection) + count = Math.Max(count, series.Count); + + rendererInfo.MinimumScale = 0; + rendererInfo.MaximumScale = count; // At least 0 + rendererInfo.MajorTick = 1; + rendererInfo.MinorTick = 0.5; + rendererInfo.MajorTickMarkWidth = DefaultMajorTickMarkWidth; + rendererInfo.MinorTickMarkWidth = DefaultMinorTickMarkWidth; + } + + /// + /// Initializes the rendererInfo's xvalues. If not set by the user xvalues will be simply numbers + /// from minimum scale + 1 to maximum scale. + /// + private void InitXValues(AxisRendererInfo rendererInfo) + { + rendererInfo.XValues = ((Chart)rendererInfo._axis._parent)._xValues; + if (rendererInfo.XValues == null) + { + rendererInfo.XValues = new XValues(); + XSeries xs = rendererInfo.XValues.AddXSeries(); + for (double i = rendererInfo.MinimumScale + 1; i <= rendererInfo.MaximumScale; ++i) + xs.Add(i.ToString(rendererInfo.TickLabelsFormat)); + } + } + + /// + /// Calculates the starting and ending y position for the minor and major tick marks. + /// + private void GetTickMarkPos(AxisRendererInfo rendererInfo, + ref double majorTickMarkStart, ref double majorTickMarkEnd, + ref double minorTickMarkStart, ref double minorTickMarkEnd) + { + double majorTickMarkWidth = rendererInfo.MajorTickMarkWidth; + double minorTickMarkWidth = rendererInfo.MinorTickMarkWidth; + XRect rect = rendererInfo.Rect; + + switch (rendererInfo.MajorTickMark) + { + case TickMarkType.Inside: + majorTickMarkStart = rect.Y; + majorTickMarkEnd = rect.Y - majorTickMarkWidth; + break; + + case TickMarkType.Outside: + majorTickMarkStart = rect.Y; + majorTickMarkEnd = rect.Y + majorTickMarkWidth; + break; + + case TickMarkType.Cross: + majorTickMarkStart = rect.Y + majorTickMarkWidth; + majorTickMarkEnd = rect.Y - majorTickMarkWidth; + break; + + case TickMarkType.None: + majorTickMarkStart = 0; + majorTickMarkEnd = 0; + break; + } + + switch (rendererInfo.MinorTickMark) + { + case TickMarkType.Inside: + minorTickMarkStart = rect.Y; + minorTickMarkEnd = rect.Y - minorTickMarkWidth; + break; + + case TickMarkType.Outside: + minorTickMarkStart = rect.Y; + minorTickMarkEnd = rect.Y + minorTickMarkWidth; + break; + + case TickMarkType.Cross: + minorTickMarkStart = rect.Y + minorTickMarkWidth; + minorTickMarkEnd = rect.Y - minorTickMarkWidth; + break; + + case TickMarkType.None: + minorTickMarkStart = 0; + minorTickMarkEnd = 0; + break; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalYAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalYAxisRenderer.cs new file mode 100644 index 00000000..bea856d9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/HorizontalYAxisRenderer.cs @@ -0,0 +1,315 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a Y axis renderer used for charts of type Bar2D. + /// + internal class HorizontalYAxisRenderer : YAxisRenderer + { + /// + /// Initializes a new instance of the HorizontalYAxisRenderer class with the + /// specified renderer parameters. + /// + internal HorizontalYAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns a initialized rendererInfo based on the Y axis. + /// + internal override RendererInfo Init() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + XGraphics gfx = _rendererParms.Graphics; + + AxisRendererInfo yari = new AxisRendererInfo(); + yari._axis = chart._yAxis; + InitScale(yari); + if (yari._axis != null) + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + InitTickLabels(yari, cri.DefaultFont); + InitAxisTitle(yari, cri.DefaultFont); + InitAxisLineFormat(yari); + InitGridlines(yari); + } + return yari; + } + + /// + /// Calculates the space used for the Y axis. + /// + internal override void Format() + { + AxisRendererInfo yari = ((ChartRendererInfo)_rendererParms.RendererInfo).yAxisRendererInfo; + if (yari._axis != null) + { + XGraphics gfx = _rendererParms.Graphics; + + XSize size = new XSize(0, 0); + + // height of all ticklabels + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double yMajorTick = yari.MajorTick; + double lineHeight = Double.MinValue; + XSize labelSize = new XSize(0, 0); + for (double y = yMin; y <= yMax; y += yMajorTick) + { + string str = y.ToString(yari.TickLabelsFormat); + labelSize = gfx.MeasureString(str, yari.TickLabelsFont); + size.Width += labelSize.Width; + size.Height = Math.Max(labelSize.Height, size.Height); + lineHeight = Math.Max(lineHeight, labelSize.Width); + } + + // add space for tickmarks + size.Height += yari.MajorTickMarkWidth * 1.5; + + // Measure axis title + XSize titleSize = new XSize(0, 0); + if (yari._axisTitleRendererInfo != null) + { + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + parms.RendererInfo = yari; + AxisTitleRenderer atr = new AxisTitleRenderer(parms); + atr.Format(); + titleSize.Height = yari._axisTitleRendererInfo.Height; + titleSize.Width = yari._axisTitleRendererInfo.Width; + } + + yari.Height = size.Height + titleSize.Height; + yari.Width = Math.Max(size.Width, titleSize.Width); + + yari.InnerRect = yari.Rect; + yari.LabelSize = labelSize; + } + } + + /// + /// Draws the vertical Y axis. + /// + internal override void Draw() + { + AxisRendererInfo yari = ((ChartRendererInfo)_rendererParms.RendererInfo).yAxisRendererInfo; + + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double yMajorTick = yari.MajorTick; + double yMinorTick = yari.MinorTick; + + XMatrix matrix = new XMatrix(); + matrix.TranslatePrepend(-yMin, -yari.Y); + matrix.Scale(yari.InnerRect.Width / (yMax - yMin), 1, XMatrixOrder.Append); + matrix.Translate(yari.X, yari.Y, XMatrixOrder.Append); + + // Draw axis. + // First draw tick marks, second draw axis. + double majorTickMarkStart = 0, majorTickMarkEnd = 0, + minorTickMarkStart = 0, minorTickMarkEnd = 0; + GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); + + XGraphics gfx = _rendererParms.Graphics; + LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); + XPoint[] points = new XPoint[2]; + if (yari.MinorTickMark != TickMarkType.None) + { + for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) + { + points[0].X = y; + points[0].Y = minorTickMarkStart; + points[1].X = y; + points[1].Y = minorTickMarkEnd; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + XStringFormat xsf = new XStringFormat(); + xsf.LineAlignment = XLineAlignment.Near; + int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; + for (int i = 0; i < countTickLabels; ++i) + { + double y = yMin + yMajorTick * i; + string str = y.ToString(yari.TickLabelsFormat); + + XSize labelSize = gfx.MeasureString(str, yari.TickLabelsFont); + if (yari.MajorTickMark != TickMarkType.None) + { + labelSize.Height += 1.5f * yari.MajorTickMarkWidth; + points[0].X = y; + points[0].Y = majorTickMarkStart; + points[1].X = y; + points[1].Y = majorTickMarkEnd; + matrix.TransformPoints(points); + lineFormatRenderer.DrawLine(points[0], points[1]); + } + + XPoint[] layoutText = new XPoint[1]; + layoutText[0].X = y; + layoutText[0].Y = yari.Y + 1.5 * yari.MajorTickMarkWidth; + matrix.TransformPoints(layoutText); + layoutText[0].X -= labelSize.Width / 2; // Center text vertically. + gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0], xsf); + } + + if (yari.LineFormat != null) + { + points[0].X = yMin; + points[0].Y = yari.Y; + points[1].X = yMax; + points[1].Y = yari.Y; + matrix.TransformPoints(points); + if (yari.MajorTickMark != TickMarkType.None) + { + // yMax is at the upper side of the axis + points[0].X -= yari.LineFormat.Width / 2; + points[1].X += yari.LineFormat.Width / 2; + } + lineFormatRenderer.DrawLine(points[0], points[1]); + } + + // Draw axis title + if (yari._axisTitleRendererInfo != null) + { + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + parms.RendererInfo = yari; + XRect rcTitle = yari.Rect; + rcTitle.Height = yari._axisTitleRendererInfo.Height; + rcTitle.Y += yari.Rect.Height - rcTitle.Height; + yari._axisTitleRendererInfo.Rect = rcTitle; + AxisTitleRenderer atr = new AxisTitleRenderer(parms); + atr.Draw(); + } + } + + /// + /// Calculates all values necessary for scaling the axis like minimum/maximum scale or + /// minor/major tick. + /// + private void InitScale(AxisRendererInfo rendererInfo) + { + double yMin, yMax; + CalcYAxis(out yMin, out yMax); + FineTuneYAxis(rendererInfo, yMin, yMax); + + rendererInfo.MajorTickMarkWidth = DefaultMajorTickMarkWidth; + rendererInfo.MinorTickMarkWidth = DefaultMinorTickMarkWidth; + } + + /// + /// Gets the top and bottom position of the major and minor tick marks depending on the + /// tick mark type. + /// + private void GetTickMarkPos(AxisRendererInfo rendererInfo, + ref double majorTickMarkStart, ref double majorTickMarkEnd, + ref double minorTickMarkStart, ref double minorTickMarkEnd) + { + double majorTickMarkWidth = rendererInfo.MajorTickMarkWidth; + double minorTickMarkWidth = rendererInfo.MinorTickMarkWidth; + double y = rendererInfo.Rect.Y; + + switch (rendererInfo.MajorTickMark) + { + case TickMarkType.Inside: + majorTickMarkStart = y - majorTickMarkWidth; + majorTickMarkEnd = y; + break; + + case TickMarkType.Outside: + majorTickMarkStart = y; + majorTickMarkEnd = y + majorTickMarkWidth; + break; + + case TickMarkType.Cross: + majorTickMarkStart = y - majorTickMarkWidth; + majorTickMarkEnd = y + majorTickMarkWidth; + break; + + //TickMarkType.None: + default: + majorTickMarkStart = 0; + majorTickMarkEnd = 0; + break; + } + + switch (rendererInfo.MinorTickMark) + { + case TickMarkType.Inside: + minorTickMarkStart = y - minorTickMarkWidth; + minorTickMarkEnd = y; + break; + + case TickMarkType.Outside: + minorTickMarkStart = y; + minorTickMarkEnd = y + minorTickMarkWidth; + break; + + case TickMarkType.Cross: + minorTickMarkStart = y - minorTickMarkWidth; + minorTickMarkEnd = y + minorTickMarkWidth; + break; + + //TickMarkType.None: + default: + minorTickMarkStart = 0; + minorTickMarkEnd = 0; + break; + } + } + + /// + /// Determines the smallest and the largest number from all series of the chart. + /// + protected virtual void CalcYAxis(out double yMin, out double yMax) + { + yMin = double.MaxValue; + yMax = double.MinValue; + + foreach (Series series in ((Chart)_rendererParms.DrawingItem).SeriesCollection) + { + foreach (Point point in series.Elements) + { + if (!double.IsNaN(point._value)) + { + yMin = Math.Min(yMin, point.Value); + yMax = Math.Max(yMax, point.Value); + } + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendEntryRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendEntryRenderer.cs new file mode 100644 index 00000000..872f937e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendEntryRenderer.cs @@ -0,0 +1,144 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the renderer for a legend entry. + /// + internal class LegendEntryRenderer : Renderer + { + /// + /// Initializes a new instance of the LegendEntryRenderer class with the specified renderer + /// parameters. + /// + internal LegendEntryRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used by the legend entry. + /// + internal override void Format() + { + XGraphics gfx = _rendererParms.Graphics; + LegendEntryRendererInfo leri = (LegendEntryRendererInfo)_rendererParms.RendererInfo; + + // Initialize + leri.MarkerArea.Width = MaxLegendMarkerWidth; + leri.MarkerArea.Height = MaxLegendMarkerHeight; + leri.MarkerSize = new XSize(); + leri.MarkerSize.Width = leri.MarkerArea.Width; + leri.MarkerSize.Height = leri.MarkerArea.Height; + if (leri._seriesRendererInfo._series._chartType == ChartType.Line) + leri.MarkerArea.Width *= 3; + leri.Width = leri.MarkerArea.Width; + leri.Height = leri.MarkerArea.Height; + + if (leri.EntryText != "") + { + leri.TextSize = gfx.MeasureString(leri.EntryText, leri._legendRendererInfo.Font); + if (leri._seriesRendererInfo._series._chartType == ChartType.Line) + { + leri.MarkerSize.Width = leri._seriesRendererInfo._markerRendererInfo.MarkerSize.Value; + leri.MarkerArea.Width = Math.Max(3 * leri.MarkerSize.Width, leri.MarkerArea.Width); + } + + leri.MarkerArea.Height = Math.Min(leri.MarkerArea.Height, leri.TextSize.Height); + leri.MarkerSize.Height = Math.Min(leri.MarkerSize.Height, leri.TextSize.Height); + leri.Width = leri.TextSize.Width + leri.MarkerArea.Width + SpacingBetweenMarkerAndText; + leri.Height = leri.TextSize.Height; + } + } + + /// + /// Draws one legend entry. + /// + internal override void Draw() + { + XGraphics gfx = _rendererParms.Graphics; + LegendEntryRendererInfo leri = (LegendEntryRendererInfo)_rendererParms.RendererInfo; + + XRect rect; + if (leri._seriesRendererInfo._series._chartType == ChartType.Line) + { + // Draw line. + XPoint posLineStart = new XPoint(leri.X, leri.Y + leri.Height / 2); + XPoint posLineEnd = new XPoint(leri.X + leri.MarkerArea.Width, leri.Y + leri.Height / 2); + gfx.DrawLine(new XPen(((XSolidBrush)leri.MarkerBrush).Color), posLineStart, posLineEnd); + + // Draw marker. + double x = leri.X + leri.MarkerArea.Width / 2; + XPoint posMarker = new XPoint(x, leri.Y + leri.Height / 2); + MarkerRenderer.Draw(gfx, posMarker, leri._seriesRendererInfo._markerRendererInfo); + } + else + { + // Draw series rectangle for column, bar or pie charts. + rect = new XRect(leri.X, leri.Y, leri.MarkerArea.Width, leri.MarkerArea.Height); + rect.Y += (leri.Height - leri.MarkerArea.Height) / 2; + gfx.DrawRectangle(leri.MarkerPen, leri.MarkerBrush, rect); + } + + // Draw text + if (leri.EntryText.Length > 0) + { + rect = leri.Rect; + rect.X += leri.MarkerArea.Width + LegendEntryRenderer.SpacingBetweenMarkerAndText; + XStringFormat format = new XStringFormat(); + format.LineAlignment = XLineAlignment.Near; + gfx.DrawString(leri.EntryText, leri._legendRendererInfo.Font, + leri._legendRendererInfo.FontColor, rect, format); + } + } + + /// + /// Absolute width for markers (including line) in point. + /// + private const double MarkerWidth = 4.3; // 1.5 mm + + /// + /// Maximum legend marker width in point. + /// + private const double MaxLegendMarkerWidth = 7; // 2.5 mm + + /// + /// Maximum legend marker height in point. + /// + private const double MaxLegendMarkerHeight = 7; // 2.5 mm + + /// + /// Insert spacing between marker and text in point. + /// + private const double SpacingBetweenMarkerAndText = 4.3; // 1.5 mm + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendRenderer.cs new file mode 100644 index 00000000..7f24f585 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LegendRenderer.cs @@ -0,0 +1,179 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the legend renderer for all chart types. + /// + internal abstract class LegendRenderer : Renderer + { + /// + /// Initializes a new instance of the LegendRenderer class with the specified renderer parameters. + /// + internal LegendRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Layouts and calculates the space used by the legend. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + LegendRendererInfo lri = cri.legendRendererInfo; + if (lri == null) + return; + + RendererParameters parms = new RendererParameters(); + parms.Graphics = _rendererParms.Graphics; + + bool verticalLegend = (lri._legend._docking == DockingType.Left || lri._legend._docking == DockingType.Right); + XSize maxMarkerArea = new XSize(); + LegendEntryRenderer ler = new LegendEntryRenderer(parms); + foreach (LegendEntryRendererInfo leri in lri.Entries) + { + parms.RendererInfo = leri; + ler.Format(); + + maxMarkerArea.Width = Math.Max(leri.MarkerArea.Width, maxMarkerArea.Width); + maxMarkerArea.Height = Math.Max(leri.MarkerArea.Height, maxMarkerArea.Height); + + if (verticalLegend) + { + lri.Width = Math.Max(lri.Width, leri.Width); + lri.Height += leri.Height; + } + else + { + lri.Width += leri.Width; + lri.Height = Math.Max(lri.Height, leri.Height); + } + } + + // Add padding to left, right, top and bottom + int paddingFactor = 1; + if (lri.BorderPen != null) + paddingFactor = 2; + lri.Width += (LegendRenderer.LeftPadding + LegendRenderer.RightPadding) * paddingFactor; + lri.Height += (LegendRenderer.TopPadding + LegendRenderer.BottomPadding) * paddingFactor; + if (verticalLegend) + lri.Height += LegendRenderer.EntrySpacing * (lri.Entries.Length - 1); + else + lri.Width += LegendRenderer.EntrySpacing * (lri.Entries.Length - 1); + + foreach (LegendEntryRendererInfo leri in lri.Entries) + leri.MarkerArea = maxMarkerArea; + } + + /// + /// Draws the legend. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + LegendRendererInfo lri = cri.legendRendererInfo; + if (lri == null) + return; + + XGraphics gfx = _rendererParms.Graphics; + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + + LegendEntryRenderer ler = new LegendEntryRenderer(parms); + + bool verticalLegend = (lri._legend._docking == DockingType.Left || lri._legend._docking == DockingType.Right); + int paddingFactor = 1; + if (lri.BorderPen != null) + paddingFactor = 2; + XRect legendRect = lri.Rect; + legendRect.X += LegendRenderer.LeftPadding * paddingFactor; + legendRect.Y += LegendRenderer.TopPadding * paddingFactor; + foreach (LegendEntryRendererInfo leri in cri.legendRendererInfo.Entries) + { + XRect entryRect = legendRect; + entryRect.Width = leri.Width; + entryRect.Height = leri.Height; + + leri.Rect = entryRect; + parms.RendererInfo = leri; + ler.Draw(); + + if (verticalLegend) + legendRect.Y += entryRect.Height + LegendRenderer.EntrySpacing; + else + legendRect.X += entryRect.Width + LegendRenderer.EntrySpacing; + } + + // Draw border around legend + if (lri.BorderPen != null) + { + XRect borderRect = lri.Rect; + borderRect.X += LegendRenderer.LeftPadding; + borderRect.Y += LegendRenderer.TopPadding; + borderRect.Width -= LegendRenderer.LeftPadding + LegendRenderer.RightPadding; + borderRect.Height -= LegendRenderer.TopPadding + LegendRenderer.BottomPadding; + gfx.DrawRectangle(lri.BorderPen, borderRect); + } + } + + /// + /// Used to insert a padding on the left. + /// + protected const double LeftPadding = 6; + + /// + /// Used to insert a padding on the right. + /// + protected const double RightPadding = 6; + + /// + /// Used to insert a padding at the top. + /// + protected const double TopPadding = 6; + + /// + /// Used to insert a padding at the bottom. + /// + protected const double BottomPadding = 6; + + /// + /// Used to insert a padding between entries. + /// + protected const double EntrySpacing = 5; + + /// + /// Default line width used for the legend's border. + /// + protected const double DefaultLineWidth = 0.14; // 0.05 mm + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineChartRenderer.cs new file mode 100644 index 00000000..7ab29384 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineChartRenderer.cs @@ -0,0 +1,196 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a line chart renderer. + /// + internal class LineChartRenderer : ColumnLikeChartRenderer + { + /// + /// Initializes a new instance of the LineChartRenderer class with the specified renderer parameters. + /// + internal LineChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = new ChartRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeriesRendererInfo(); + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + cri.xAxisRendererInfo = (AxisRendererInfo)xar.Init(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + cri.yAxisRendererInfo = (AxisRendererInfo)yar.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + LinePlotAreaRenderer lpar = new LinePlotAreaRenderer(_rendererParms); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)lpar.Init(); + + return cri; + } + + /// + /// Layouts and calculates the space used by the line chart. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Format(); + + // axes + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Format(); + + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Format(); + + // Calculate rects and positions. + CalcLayout(); + + // Calculated remaining plot area, now it's safe to format. + LinePlotAreaRenderer lpar = new LinePlotAreaRenderer(_rendererParms); + lpar.Format(); + } + + /// + /// Draws the line chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new ColumnLikeLegendRenderer(_rendererParms); + lr.Draw(); + + // Draw wall. + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + // Draw gridlines. + GridlinesRenderer glr = new ColumnLikeGridlinesRenderer(_rendererParms); + glr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + // Draw line chart's plot area. + LinePlotAreaRenderer lpar = new LinePlotAreaRenderer(_rendererParms); + lpar.Draw(); + + // Draw x- and y-axis. + if (cri.xAxisRendererInfo._axis != null) + { + AxisRenderer xar = new HorizontalXAxisRenderer(_rendererParms); + xar.Draw(); + } + + if (cri.yAxisRendererInfo._axis != null) + { + AxisRenderer yar = new VerticalYAxisRenderer(_rendererParms); + yar.Draw(); + } + } + + /// + /// Initializes all necessary data to draw a series for a line chart. + /// + private void InitSeriesRendererInfo() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + SeriesCollection seriesColl = cri._chart.SeriesCollection; + cri.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + sri._series = seriesColl[idx]; + cri.seriesRendererInfos[idx] = sri; + } + InitSeries(); + } + + /// + /// Initializes all necessary data to draw a series for a line chart. + /// + internal void InitSeries() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int seriesIndex = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._series._markerBackgroundColor.IsEmpty) + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, LineColors.Item(seriesIndex), ChartRenderer.DefaultSeriesLineWidth); + else + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, sri._series._markerBackgroundColor, ChartRenderer.DefaultSeriesLineWidth); + sri.LineFormat.LineJoin = XLineJoin.Bevel; + + MarkerRendererInfo mri = new MarkerRendererInfo(); + sri._markerRendererInfo = mri; + + mri.MarkerForegroundColor = sri._series._markerForegroundColor; + if (mri.MarkerForegroundColor.IsEmpty) + mri.MarkerForegroundColor = XColors.Black; + + mri.MarkerBackgroundColor = sri._series._markerBackgroundColor; + if (mri.MarkerBackgroundColor.IsEmpty) + mri.MarkerBackgroundColor = sri.LineFormat.Color; + + mri.MarkerSize = sri._series._markerSize; + if (mri.MarkerSize == 0) + mri.MarkerSize = 7; + + if (!sri._series._markerStyleInitialized) + //mri.MarkerStyle = (MarkerStyle)(seriesIndex % (Enum.GetNames(typeof(MarkerStyle)).Length - 1) + 1); + mri.MarkerStyle = (MarkerStyle)(seriesIndex % (10 - 1) + 1); + else + mri.MarkerStyle = sri._series._markerStyle; + + ++seriesIndex; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineFormatRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineFormatRenderer.cs new file mode 100644 index 00000000..09a1f9e5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LineFormatRenderer.cs @@ -0,0 +1,118 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a renderer specialized to draw lines in various styles, colors and widths. + /// + class LineFormatRenderer + { + /// + /// Initializes a new instance of the LineFormatRenderer class with the specified graphics, line format + /// and default width. + /// + public LineFormatRenderer(XGraphics gfx, LineFormat lineFormat, double defaultWidth) + { + _gfx = gfx; + bool visible = false; + double width = 0; + + if (lineFormat != null) + { + width = lineFormat._width; + if (width == 0 && !lineFormat.Color.IsEmpty) + width = defaultWidth; + visible = lineFormat.Visible || width > 0 || !lineFormat.Color.IsEmpty; + } + + if (visible) + { + _pen = new XPen(lineFormat.Color, width); + _pen.DashStyle = lineFormat.DashStyle; + } + } + + /// + /// Initializes a new instance of the LineFormatRenderer class with the specified graphics and + /// line format. + /// + public LineFormatRenderer(XGraphics gfx, LineFormat lineFormat) : + this(gfx, lineFormat, 0) + { } + + /// + /// Initializes a new instance of the LineFormatRenderer class with the specified graphics and pen. + /// + public LineFormatRenderer(XGraphics gfx, XPen pen) + { + _gfx = gfx; + _pen = pen; + } + + /// + /// Draws a line from point pt0 to point pt1. + /// + public void DrawLine(XPoint pt0, XPoint pt1) + { + if (_pen != null) + _gfx.DrawLine(_pen, pt0, pt1); + } + + /// + /// Draws a line specified by rect. + /// + public void DrawRectangle(XRect rect) + { + if (_pen != null) + _gfx.DrawRectangle(_pen, rect); + } + + /// + /// Draws a line specified by path. + /// + public void DrawPath(XGraphicsPath path) + { + if (_pen != null) + _gfx.DrawPath(_pen, path); + } + + /// + /// Surface to draw the line. + /// + readonly XGraphics _gfx; + + /// + /// Pen used to draw the line. + /// + readonly XPen _pen; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LinePlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LinePlotAreaRenderer.cs new file mode 100644 index 00000000..8dbad291 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/LinePlotAreaRenderer.cs @@ -0,0 +1,99 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Renders the plot area used by line charts. + /// + internal class LinePlotAreaRenderer : ColumnLikePlotAreaRenderer + { + /// + /// Initializes a new instance of the LinePlotAreaRenderer class with the + /// specified renderer parameters. + /// + internal LinePlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the content of the line plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; + if (plotAreaRect.IsEmpty) + return; + + XGraphics gfx = _rendererParms.Graphics; + XGraphicsState state = gfx.Save(); + //gfx.SetClip(plotAreaRect, XCombineMode.Intersect); + gfx.IntersectClip(plotAreaRect); + + //TODO Treat null values correctly. + // Points can be missing. Treat null values accordingly (NotPlotted, Interpolate etc.) + + // Draw lines and markers for each data series. + XMatrix matrix = cri.plotAreaRendererInfo._matrix; + + double xMajorTick = cri.xAxisRendererInfo.MajorTick; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + int count = sri._series.Elements.Count; + XPoint[] points = new XPoint[count]; + for (int idx = 0; idx < count; idx++) + { + double v = sri._series.Elements[idx].Value; + if (double.IsNaN(v)) + v = 0; + points[idx] = new XPoint(idx + xMajorTick / 2, v); + } + matrix.TransformPoints(points); + gfx.DrawLines(sri.LineFormat, points); + DrawMarker(gfx, points, sri); + } + + //gfx.ResetClip(); + gfx.Restore(state); + } + + /// + /// Draws all markers given in rendererInfo at the positions specified by points. + /// + void DrawMarker(XGraphics graphics, XPoint[] points, SeriesRendererInfo rendererInfo) + { + foreach (XPoint pos in points) + MarkerRenderer.Draw(graphics, pos, rendererInfo._markerRendererInfo); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/MarkerRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/MarkerRenderer.cs new file mode 100644 index 00000000..96457e16 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/MarkerRenderer.cs @@ -0,0 +1,189 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a renderer for markers in line charts and legends. + /// + internal class MarkerRenderer + { + /// + /// Draws the marker given through rendererInfo at the specified position. Position specifies + /// the center of the marker. + /// + internal static void Draw(XGraphics graphics, XPoint pos, MarkerRendererInfo rendererInfo) + { +#if SILVERLIGHT + return; // BUG: Code crashs Silverlight Path class. +#pragma warning disable 0162 +#endif + + if (rendererInfo.MarkerStyle == MarkerStyle.None) + return; + + double size = rendererInfo.MarkerSize; + double size2 = size / 2; + double x0, y0, x1, y1; + double g; + + XPen foreground = new XPen(rendererInfo.MarkerForegroundColor, 0.5); + XBrush background = new XSolidBrush(rendererInfo.MarkerBackgroundColor); + + XGraphicsPath gp = new XGraphicsPath(); + switch (rendererInfo.MarkerStyle) + { + case MarkerStyle.Square: + x0 = pos.X - size2; + y0 = pos.Y - size2; + x1 = pos.X + size2; + y1 = pos.Y + size2; + gp.AddLine(x0, y0, x1, y0); + gp.AddLine(x1, y0, x1, y1); + gp.AddLine(x1, y1, x0, y1); + gp.AddLine(x0, y1, x0, y0); + break; + + case MarkerStyle.Diamond: + gp.AddLine(x1 = pos.X + size2, pos.Y, pos.X, y0 = pos.Y - size2); + gp.AddLine(pos.X, y0, x0 = pos.X - size2, pos.Y); + gp.AddLine(x0, pos.Y, pos.X, y1 = pos.Y + size2); + gp.AddLine(pos.X, y1, x1, pos.Y); + break; + + case MarkerStyle.Triangle: + y0 = pos.Y + size / 2; + y1 = pos.Y - size / 2; + g = Math.Sqrt(size * size * 4 / 3) / 2; + gp.AddLine(pos.X, y1, pos.X + g, y0); + gp.AddLine(pos.X + g, y0, pos.X - g, y0); + gp.AddLine(pos.X - g, y0, pos.X, y1); + break; + + case MarkerStyle.Plus: + g = size2 / 4; + gp.AddLine(pos.X - size2, pos.Y + g, pos.X - g, pos.Y + g); + gp.AddLine(pos.X - g, pos.Y + g, pos.X - g, pos.Y + size2); + gp.AddLine(pos.X - g, pos.Y + size2, pos.X + g, pos.Y + size2); + gp.AddLine(pos.X + g, pos.Y + size2, pos.X + g, pos.Y + g); + gp.AddLine(pos.X + g, pos.Y + g, pos.X + size2, pos.Y + g); + gp.AddLine(pos.X + size2, pos.Y + g, pos.X + size2, pos.Y - g); + gp.AddLine(pos.X + size2, pos.Y - g, pos.X + g, pos.Y - g); + gp.AddLine(pos.X + g, pos.Y - g, pos.X + g, pos.Y - size2); + gp.AddLine(pos.X + g, pos.Y - size2, pos.X - g, pos.Y - size2); + gp.AddLine(pos.X - g, pos.Y - size2, pos.X - g, pos.Y - g); + gp.AddLine(pos.X - g, pos.Y - g, pos.X - size2, pos.Y - g); + gp.AddLine(pos.X - size2, pos.Y - g, pos.X - size2, pos.Y + g); + break; + + case MarkerStyle.Circle: + case MarkerStyle.Dot: + x0 = pos.X - size2; + y0 = pos.Y - size2; + gp.AddEllipse(x0, y0, size, size); + break; + + case MarkerStyle.Dash: + x0 = pos.X - size2; + y0 = pos.Y - size2 / 3; + x1 = pos.X + size2; + y1 = pos.Y + size2 / 3; + gp.AddLine(x0, y0, x1, y0); + gp.AddLine(x1, y0, x1, y1); + gp.AddLine(x1, y1, x0, y1); + gp.AddLine(x0, y1, x0, y0); + break; + + case MarkerStyle.X: + g = size / 4; + gp.AddLine(pos.X - size2 + g, pos.Y - size2, pos.X, pos.Y - g); + gp.AddLine(pos.X, pos.Y - g, pos.X + size2 - g, pos.Y - size2); + gp.AddLine(pos.X + size2 - g, pos.Y - size2, pos.X + size2, pos.Y - size2 + g); + gp.AddLine(pos.X + size2, pos.Y - size2 + g, pos.X + g, pos.Y); + gp.AddLine(pos.X + g, pos.Y, pos.X + size2, pos.Y + size2 - g); + gp.AddLine(pos.X + size2, pos.Y + size2 - g, pos.X + size2 - g, pos.Y + size2); + gp.AddLine(pos.X + size2 - g, pos.Y + size2, pos.X, pos.Y + g); + gp.AddLine(pos.X, pos.Y + g, pos.X - size2 + g, pos.Y + size2); + gp.AddLine(pos.X - size2 + g, pos.Y + size2, pos.X - size2, pos.Y + size2 - g); + gp.AddLine(pos.X - size2, pos.Y + size2 - g, pos.X - g, pos.Y); + gp.AddLine(pos.X - g, pos.Y, pos.X - size2, pos.Y - size2 + g); + break; + + case MarkerStyle.Star: + { + XPoint[] points = new XPoint[10]; + + double radStep = 2 * Math.PI / 5; + double outerCircle = size / 2; + double innerCircle = size / 5; + // outer circle + double rad = -(Math.PI / 2); // 90 + for (int idx = 0; idx < 10; idx += 2) + { + points[idx].X = pos.X + outerCircle * Math.Cos(rad); + points[idx].Y = pos.Y + outerCircle * Math.Sin(rad); + rad += radStep; + } + + // inner circle + rad = -(Math.PI / 4); // 45 + double x = innerCircle * Math.Cos(rad); + double y = innerCircle * Math.Sin(rad); + points[1].X = pos.X + x; + points[1].Y = pos.Y + y; + points[9].X = pos.X - x; + points[9].Y = pos.Y + y; + rad += radStep; + x = innerCircle * Math.Cos(rad); + y = innerCircle * Math.Sin(rad); + points[3].X = pos.X + x; + points[3].Y = pos.Y + y; + points[7].X = pos.X - x; + points[7].Y = pos.Y + y; + rad += radStep; + y = innerCircle * Math.Sin(rad); + points[5].X = pos.X; + points[5].Y = pos.Y + y; + gp.AddLines(points); + } + break; + } + + gp.CloseFigure(); + if (rendererInfo.MarkerStyle != MarkerStyle.Dot) + { + graphics.DrawPath(background, gp); + graphics.DrawPath(foreground, gp); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieChartRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieChartRenderer.cs new file mode 100644 index 00000000..090f43ae --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieChartRenderer.cs @@ -0,0 +1,177 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a pie chart renderer. + /// + internal class PieChartRenderer : ChartRenderer + { + /// + /// Initializes a new instance of the PieChartRenderer class with the + /// specified renderer parameters. + /// + internal PieChartRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized and renderer specific rendererInfo. + /// + internal override RendererInfo Init() + { + ChartRendererInfo cri = new ChartRendererInfo(); + cri._chart = (Chart)_rendererParms.DrawingItem; + _rendererParms.RendererInfo = cri; + + InitSeries(cri); + + LegendRenderer lr = new PieLegendRenderer(_rendererParms); + cri.legendRendererInfo = (LegendRendererInfo)lr.Init(); + + PlotArea plotArea = cri._chart.PlotArea; + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + cri.plotAreaRendererInfo = (PlotAreaRendererInfo)renderer.Init(); + + DataLabelRenderer dlr = new PieDataLabelRenderer(_rendererParms); + dlr.Init(); + + return cri; + } + + /// + /// Layouts and calculates the space used by the pie chart. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + LegendRenderer lr = new PieLegendRenderer(_rendererParms); + lr.Format(); + + // Calculate rects and positions. + XRect chartRect = LayoutLegend(); + cri.plotAreaRendererInfo.Rect = chartRect; + double edge = Math.Min(chartRect.Width, chartRect.Height); + cri.plotAreaRendererInfo.X += (chartRect.Width - edge) / 2; + cri.plotAreaRendererInfo.Y += (chartRect.Height - edge) / 2; + cri.plotAreaRendererInfo.Width = edge; + cri.plotAreaRendererInfo.Height = edge; + + DataLabelRenderer dlr = new PieDataLabelRenderer(_rendererParms); + dlr.Format(); + + // Calculated remaining plot area, now it's safe to format. + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Format(); + + dlr.CalcPositions(); + } + + /// + /// Draws the pie chart. + /// + internal override void Draw() + { + LegendRenderer lr = new PieLegendRenderer(_rendererParms); + lr.Draw(); + + WallRenderer wr = new WallRenderer(_rendererParms); + wr.Draw(); + + PlotAreaBorderRenderer pabr = new PlotAreaBorderRenderer(_rendererParms); + pabr.Draw(); + + PlotAreaRenderer renderer = GetPlotAreaRenderer(); + renderer.Draw(); + + DataLabelRenderer dlr = new PieDataLabelRenderer(_rendererParms); + dlr.Draw(); + } + + /// + /// Returns the specific plot area renderer. + /// + private PlotAreaRenderer GetPlotAreaRenderer() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + switch (chart._type) + { + case ChartType.Pie2D: + return new PieClosedPlotAreaRenderer(_rendererParms); + + case ChartType.PieExploded2D: + return new PieExplodedPlotAreaRenderer(_rendererParms); + } + return null; + } + + /// + /// Initializes all necessary data to draw a series for a pie chart. + /// + protected void InitSeries(ChartRendererInfo rendererInfo) + { + SeriesCollection seriesColl = rendererInfo._chart.SeriesCollection; + rendererInfo.seriesRendererInfos = new SeriesRendererInfo[seriesColl.Count]; + for (int idx = 0; idx < seriesColl.Count; ++idx) + { + SeriesRendererInfo sri = new SeriesRendererInfo(); + rendererInfo.seriesRendererInfos[idx] = sri; + sri._series = seriesColl[idx]; + + sri.LineFormat = Converter.ToXPen(sri._series._lineFormat, XColors.Black, ChartRenderer.DefaultSeriesLineWidth); + sri.FillFormat = Converter.ToXBrush(sri._series._fillFormat, ColumnColors.Item(idx)); + + sri._pointRendererInfos = new SectorRendererInfo[sri._series._seriesElements.Count]; + for (int pointIdx = 0; pointIdx < sri._pointRendererInfos.Length; ++pointIdx) + { + PointRendererInfo pri = new SectorRendererInfo(); + Point point = sri._series._seriesElements[pointIdx]; + pri.Point = point; + if (point != null) + { + pri.LineFormat = sri.LineFormat; + if (point._lineFormat != null && !point._lineFormat._color.IsEmpty) + pri.LineFormat = new XPen(point._lineFormat._color); + if (point._fillFormat != null && !point._fillFormat._color.IsEmpty) + pri.FillFormat = new XSolidBrush(point._fillFormat._color); + else + pri.FillFormat = new XSolidBrush(PieColors.Item(pointIdx)); + pri.LineFormat.LineJoin = XLineJoin.Round; + } + sri._pointRendererInfos[pointIdx] = pri; + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieClosedPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieClosedPlotAreaRenderer.cs new file mode 100644 index 00000000..9a65ab39 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieClosedPlotAreaRenderer.cs @@ -0,0 +1,103 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a closed pie plot area renderer. + /// + internal class PieClosedPlotAreaRenderer : PiePlotAreaRenderer + { + /// + /// Initializes a new instance of the PiePlotAreaRenderer class + /// with the specified renderer parameters. + /// + internal PieClosedPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculate angles for each sector. + /// + protected override void CalcSectors() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + + double sumValues = sri.SumOfPoints; + if (sumValues == 0) + return; + + double textMeasure = 0; + if (sri._dataLabelRendererInfo != null && sri._dataLabelRendererInfo.Position == DataLabelPosition.OutsideEnd) + { + foreach (DataLabelEntryRendererInfo dleri in sri._dataLabelRendererInfo.Entries) + { + textMeasure = Math.Max(textMeasure, dleri.Width); + textMeasure = Math.Max(textMeasure, dleri.Height); + } + } + + XRect pieRect = cri.plotAreaRendererInfo.Rect; + if (textMeasure != 0) + { + pieRect.X += textMeasure; + pieRect.Y += textMeasure; + pieRect.Width -= 2 * textMeasure; + pieRect.Height -= 2 * textMeasure; + } + + double startAngle = 270, sweepAngle = 0; + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + if (!double.IsNaN(sector.Point._value) && sector.Point._value != 0) + { + sweepAngle = 360 / (sumValues / Math.Abs(sector.Point._value)); + + sector.Rect = pieRect; + sector.StartAngle = startAngle; + sector.SweepAngle = sweepAngle; + + startAngle += sweepAngle; + } + else + { + sector.StartAngle = double.NaN; + sector.SweepAngle = double.NaN; + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieDataLabelRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieDataLabelRenderer.cs new file mode 100644 index 00000000..0ad1d32f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieDataLabelRenderer.cs @@ -0,0 +1,182 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a data label renderer for pie charts. + /// + internal class PieDataLabelRenderer : DataLabelRenderer + { + /// + /// Initializes a new instance of the PieDataLabelRenderer class with the + /// specified renderer parameters. + /// + internal PieDataLabelRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates the space used by the data labels. + /// + internal override void Format() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + if (sri._dataLabelRendererInfo == null) + return; + + double sumValues = sri.SumOfPoints; + XGraphics gfx = _rendererParms.Graphics; + + sri._dataLabelRendererInfo.Entries = new DataLabelEntryRendererInfo[sri._pointRendererInfos.Length]; + int index = 0; + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + DataLabelEntryRendererInfo dleri = new DataLabelEntryRendererInfo(); + if (sri._dataLabelRendererInfo.Type != DataLabelType.None) + { + if (sri._dataLabelRendererInfo.Type == DataLabelType.Percent) + { + double percent = 100 / (sumValues / Math.Abs(sector.Point._value)); + dleri.Text = percent.ToString(sri._dataLabelRendererInfo.Format) + "%"; + } + else if (sri._dataLabelRendererInfo.Type == DataLabelType.Value) + dleri.Text = sector.Point._value.ToString(sri._dataLabelRendererInfo.Format); + + if (dleri.Text.Length > 0) + dleri.Size = gfx.MeasureString(dleri.Text, sri._dataLabelRendererInfo.Font); + } + + sri._dataLabelRendererInfo.Entries[index++] = dleri; + } + } + + /// + /// Draws the data labels of the pie chart. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + if (sri == null || sri._dataLabelRendererInfo == null) + return; + + XGraphics gfx = _rendererParms.Graphics; + XFont font = sri._dataLabelRendererInfo.Font; + XBrush fontColor = sri._dataLabelRendererInfo.FontColor; + XStringFormat format = XStringFormats.Center; + format.LineAlignment = XLineAlignment.Center; + foreach (DataLabelEntryRendererInfo dataLabel in sri._dataLabelRendererInfo.Entries) + { + if (dataLabel.Text != null) + gfx.DrawString(dataLabel.Text, font, fontColor, dataLabel.Rect, format); + } + } + + /// + /// Calculates the data label positions specific for pie charts. + /// + internal override void CalcPositions() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XGraphics gfx = _rendererParms.Graphics; + + if (cri.seriesRendererInfos.Length > 0) + { + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + if (sri != null && sri._dataLabelRendererInfo != null) + { + int sectorIndex = 0; + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + // Determine output rectangle + double midAngle = sector.StartAngle + sector.SweepAngle / 2; + double radMidAngle = midAngle / 180 * Math.PI; + XPoint origin = new XPoint(sector.Rect.X + sector.Rect.Width / 2, + sector.Rect.Y + sector.Rect.Height / 2); + double radius = sector.Rect.Width / 2; + double halfradius = radius / 2; + + DataLabelEntryRendererInfo dleri = sri._dataLabelRendererInfo.Entries[sectorIndex++]; + switch (sri._dataLabelRendererInfo.Position) + { + case DataLabelPosition.OutsideEnd: + // Outer border of the circle. + dleri.X = origin.X + (radius * Math.Cos(radMidAngle)); + dleri.Y = origin.Y + (radius * Math.Sin(radMidAngle)); + if (dleri.X < origin.X) + dleri.X -= dleri.Width; + if (dleri.Y < origin.Y) + dleri.Y -= dleri.Height; + break; + + case DataLabelPosition.InsideEnd: + // Inner border of the circle. + dleri.X = origin.X + (radius * Math.Cos(radMidAngle)); + dleri.Y = origin.Y + (radius * Math.Sin(radMidAngle)); + if (dleri.X > origin.X) + dleri.X -= dleri.Width; + if (dleri.Y > origin.Y) + dleri.Y -= dleri.Height; + break; + + case DataLabelPosition.Center: + // Centered + dleri.X = origin.X + (halfradius * Math.Cos(radMidAngle)); + dleri.Y = origin.Y + (halfradius * Math.Sin(radMidAngle)); + dleri.X -= dleri.Width / 2; + dleri.Y -= dleri.Height / 2; + break; + + case DataLabelPosition.InsideBase: + // Aligned at the base/center of the circle + dleri.X = origin.X; + dleri.Y = origin.Y; + if (dleri.X < origin.X) + dleri.X -= dleri.Width; + if (dleri.Y < origin.Y) + dleri.Y -= dleri.Height; + break; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieExplodedPlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieExplodedPlotAreaRenderer.cs new file mode 100644 index 00000000..2addb229 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieExplodedPlotAreaRenderer.cs @@ -0,0 +1,122 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a exploded pie plot area renderer. + /// + internal class PieExplodedPlotAreaRenderer : PiePlotAreaRenderer + { + /// + /// Initializes a new instance of the PieExplodedPlotAreaRenderer class + /// with the specified renderer parameters. + /// + internal PieExplodedPlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculate angles for each sector. + /// + protected override void CalcSectors() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.seriesRendererInfos.Length == 0) + return; + + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + + double sumValues = sri.SumOfPoints; + if (sumValues == 0) + return; + + double textMeasure = 0; + if (sri._dataLabelRendererInfo != null && sri._dataLabelRendererInfo.Position == DataLabelPosition.OutsideEnd) + { + foreach (DataLabelEntryRendererInfo dleri in sri._dataLabelRendererInfo.Entries) + { + textMeasure = Math.Max(textMeasure, dleri.Width); + textMeasure = Math.Max(textMeasure, dleri.Height); + } + } + + XRect pieRect = cri.plotAreaRendererInfo.Rect; + if (textMeasure != 0) + { + pieRect.X += textMeasure; + pieRect.Y += textMeasure; + pieRect.Width -= 2 * textMeasure; + pieRect.Height -= 2 * textMeasure; + } + + XPoint origin = new XPoint(pieRect.X + pieRect.Width / 2, pieRect.Y + pieRect.Height / 2); + XRect innerRect = new XRect(); + XPoint p1 = new XPoint(); + + double midAngle = 0, sectorStartAngle = 0, sectorSweepAngle = 0, + deltaAngle = 2, startAngle = 270, sweepAngle = 0, + rInnerCircle = pieRect.Width / 15, + rOuterCircle = pieRect.Width / 2; + + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + if (!double.IsNaN(sector.Point._value) && sector.Point._value != 0) + { + sweepAngle = 360 / (sumValues / Math.Abs(sector.Point._value)); + + midAngle = startAngle + sweepAngle / 2; + sectorStartAngle = Math.Max(0, startAngle + deltaAngle); + sectorSweepAngle = Math.Max(sweepAngle, sweepAngle - deltaAngle); + + p1.X = origin.X + rInnerCircle * Math.Cos(midAngle / 180 * Math.PI); + p1.Y = origin.Y + rInnerCircle * Math.Sin(midAngle / 180 * Math.PI); + innerRect.X = p1.X - rOuterCircle + rInnerCircle; + innerRect.Y = p1.Y - rOuterCircle + rInnerCircle; + innerRect.Width = (rOuterCircle - rInnerCircle) * 2; + innerRect.Height = innerRect.Width; + + sector.Rect = innerRect; + sector.StartAngle = sectorStartAngle; + sector.SweepAngle = sectorSweepAngle; + + startAngle += sweepAngle; + } + else + { + sector.StartAngle = double.NaN; + sector.SweepAngle = double.NaN; + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieLegendRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieLegendRenderer.cs new file mode 100644 index 00000000..362b9ab7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PieLegendRenderer.cs @@ -0,0 +1,95 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the legend renderer specific to pie charts. + /// + internal class PieLegendRenderer : LegendRenderer + { + /// + /// Initializes a new instance of the PieLegendRenderer class with the specified renderer + /// parameters. + /// + internal PieLegendRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Initializes the legend's renderer info. Each data point will be represented through + /// a legend entry renderer info. + /// + internal override RendererInfo Init() + { + LegendRendererInfo lri = null; + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri._chart._legend != null) + { + lri = new LegendRendererInfo(); + lri._legend = cri._chart._legend; + + lri.Font = Converter.ToXFont(lri._legend._font, cri.DefaultFont); + lri.FontColor = new XSolidBrush(XColors.Black); + + if (lri._legend._lineFormat != null) + lri.BorderPen = Converter.ToXPen(lri._legend._lineFormat, XColors.Black, DefaultLineWidth, XDashStyle.Solid); + + XSeries xseries = null; + if (cri._chart._xValues != null) + xseries = cri._chart._xValues[0]; + + int index = 0; + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + lri.Entries = new LegendEntryRendererInfo[sri._pointRendererInfos.Length]; + foreach (PointRendererInfo pri in sri._pointRendererInfos) + { + LegendEntryRendererInfo leri = new LegendEntryRendererInfo(); + leri._seriesRendererInfo = sri; + leri._legendRendererInfo = lri; + leri.EntryText = string.Empty; + if (xseries != null) + { + if (xseries.Count > index) + leri.EntryText = xseries[index]._value; + } + else + leri.EntryText = (index + 1).ToString(); // create default/dummy entry + leri.MarkerPen = pri.LineFormat; + leri.MarkerBrush = pri.FillFormat; + + lri.Entries[index++] = leri; + } + } + return lri; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PiePlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PiePlotAreaRenderer.cs new file mode 100644 index 00000000..c0443145 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PiePlotAreaRenderer.cs @@ -0,0 +1,94 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base for all pie plot area renderer. + /// + internal abstract class PiePlotAreaRenderer : PlotAreaRenderer + { + /// + /// Initializes a new instance of the PiePlotAreaRenderer class + /// with the specified renderer parameters. + /// + internal PiePlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Layouts and calculates the space used by the pie plot area. + /// + internal override void Format() + { + CalcSectors(); + } + + /// + /// Draws the content of the pie plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + XRect plotAreaRect = cri.plotAreaRendererInfo.Rect; + if (plotAreaRect.IsEmpty) + return; + + if (cri.seriesRendererInfos.Length == 0) + return; + + XGraphics gfx = _rendererParms.Graphics; + XGraphicsState state = gfx.Save(); + + // Draw sectors. + SeriesRendererInfo sri = cri.seriesRendererInfos[0]; + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + if (!double.IsNaN(sector.StartAngle) && !double.IsNaN(sector.SweepAngle)) + gfx.DrawPie(sector.FillFormat, sector.Rect, sector.StartAngle, sector.SweepAngle); + } + + // Draw border of the sectors. + foreach (SectorRendererInfo sector in sri._pointRendererInfos) + { + if (!double.IsNaN(sector.StartAngle) && !double.IsNaN(sector.SweepAngle)) + gfx.DrawPie(sector.LineFormat, sector.Rect, sector.StartAngle, sector.SweepAngle); + } + + gfx.Restore(state); + } + + /// + /// Calculates the specific positions for each sector. + /// + protected abstract void CalcSectors(); + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaBorderRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaBorderRenderer.cs new file mode 100644 index 00000000..4f5b09ff --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaBorderRenderer.cs @@ -0,0 +1,61 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the border renderer for plot areas. + /// + internal class PlotAreaBorderRenderer : Renderer + { + /// + /// Initializes a new instance of the PlotAreaBorderRenderer class with the specified + /// renderer parameters. + /// + internal PlotAreaBorderRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the border around the plot area. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.plotAreaRendererInfo.LineFormat != null && cri.plotAreaRendererInfo.LineFormat.Width > 0) + { + XGraphics gfx = _rendererParms.Graphics; + LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, cri.plotAreaRendererInfo.LineFormat); + lineFormatRenderer.DrawRectangle(cri.plotAreaRendererInfo.Rect); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaRenderer.cs new file mode 100644 index 00000000..e08e1593 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/PlotAreaRenderer.cs @@ -0,0 +1,83 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Base class for all plot area renderers. + /// + internal abstract class PlotAreaRenderer : Renderer + { + /// + /// Initializes a new instance of the PlotAreaRenderer class with the specified renderer parameters. + /// + internal PlotAreaRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized PlotAreaRendererInfo. + /// + internal override RendererInfo Init() + { + PlotAreaRendererInfo pari = new PlotAreaRendererInfo(); + pari._plotArea = ((ChartRendererInfo)_rendererParms.RendererInfo)._chart._plotArea; + InitLineFormat(pari); + InitFillFormat(pari); + return pari; + } + + /// + /// Initializes the plot area's line format common to all derived plot area renderers. + /// If line format is given all uninitialized values will be set. + /// + protected void InitLineFormat(PlotAreaRendererInfo rendererInfo) + { + if (rendererInfo._plotArea._lineFormat != null) + rendererInfo.LineFormat = Converter.ToXPen(rendererInfo._plotArea._lineFormat, XColors.Black, DefaultLineWidth); + } + + /// + /// Initializes the plot area's fill format common to all derived plot area renderers. + /// If fill format is given all uninitialized values will be set. + /// + protected void InitFillFormat(PlotAreaRendererInfo rendererInfo) + { + if (rendererInfo._plotArea._fillFormat != null) + rendererInfo.FillFormat = Converter.ToXBrush(rendererInfo._plotArea._fillFormat, XColors.White); + } + + /// + /// Represents the default line width for the plot area's border. + /// + protected const double DefaultLineWidth = 0.15; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Renderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Renderer.cs new file mode 100644 index 00000000..3d1ef45f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/Renderer.cs @@ -0,0 +1,72 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Base class of all renderers. + /// + internal abstract class Renderer + { + /// + /// Initializes a new instance of the Renderer class with the specified renderer parameters. + /// + internal Renderer(RendererParameters rendererParms) + { + _rendererParms = rendererParms; + } + + /// + /// Derived renderer should return an initialized and renderer specific rendererInfo, + /// e. g. XAxisRenderer returns an new instance of AxisRendererInfo class. + /// + internal virtual RendererInfo Init() + { + return null; + } + + /// + /// Layouts and calculates the space used by the renderer's drawing item. + /// + internal virtual void Format() + { + // nothing to do + } + + /// + /// Draws the item. + /// + internal abstract void Draw(); + + /// + /// Holds all necessary rendering information. + /// + protected RendererParameters _rendererParms; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererInfo.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererInfo.cs new file mode 100644 index 00000000..70416b2c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererInfo.cs @@ -0,0 +1,409 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base class of all renderer infos. + /// Renderer infos are used to hold all necessary information and time consuming calculations + /// between rendering cycles. + /// + internal abstract class RendererInfo + { } + + /// + /// Base class for all renderer infos which defines an area. + /// + internal abstract class AreaRendererInfo : RendererInfo + { + /// + /// Gets or sets the x coordinate of this rectangle. + /// + internal virtual double X + { + get { return _rect.X; } + set { _rect.X = value; } + } + + /// + /// Gets or sets the y coordinate of this rectangle. + /// + internal virtual double Y + { + get { return _rect.Y; } + set { _rect.Y = value; } + } + + /// + /// Gets or sets the width of this rectangle. + /// + internal virtual double Width + { + get { return _rect.Width; } + set { _rect.Width = value; } + } + + /// + /// Gets or sets the height of this rectangle. + /// + internal virtual double Height + { + get { return _rect.Height; } + set { _rect.Height = value; } + } + + /// + /// Gets the area's size. + /// + internal XSize Size + { + get { return _rect.Size; } + set { _rect.Size = value; } + } + + /// + /// Gets the area's rectangle. + /// + internal XRect Rect + { + get { return _rect; } + set { _rect = value; } + } + XRect _rect; + } + + /// + /// A ChartRendererInfo stores information of all main parts of a chart like axis renderer info or + /// plotarea renderer info. + /// + internal class ChartRendererInfo : AreaRendererInfo + { + internal Chart _chart; + + internal AxisRendererInfo xAxisRendererInfo; + internal AxisRendererInfo yAxisRendererInfo; + //internal AxisRendererInfo zAxisRendererInfo; // not yet used + internal PlotAreaRendererInfo plotAreaRendererInfo; + internal LegendRendererInfo legendRendererInfo; + internal SeriesRendererInfo[] seriesRendererInfos; + + /// + /// Gets the chart's default font for rendering. + /// + internal XFont DefaultFont + { + get + { + return _defaultFont ?? + (_defaultFont = Converter.ToXFont(_chart._font, new XFont("Arial", 12, XFontStyle.Regular))); + } + } + XFont _defaultFont; + + /// + /// Gets the chart's default font for rendering data labels. + /// + internal XFont DefaultDataLabelFont + { + get + { + return _defaultDataLabelFont ?? + (_defaultDataLabelFont = Converter.ToXFont(_chart._font, new XFont("Arial", 10, XFontStyle.Regular))); + } + } + XFont _defaultDataLabelFont; + } + + /// + /// A CombinationRendererInfo stores information for rendering combination of charts. + /// + internal class CombinationRendererInfo : ChartRendererInfo + { + internal SeriesRendererInfo[] _commonSeriesRendererInfos; + internal SeriesRendererInfo[] _areaSeriesRendererInfos; + internal SeriesRendererInfo[] _columnSeriesRendererInfos; + internal SeriesRendererInfo[] _lineSeriesRendererInfos; + } + + /// + /// PointRendererInfo is used to render one single data point which is part of a data series. + /// + internal class PointRendererInfo : RendererInfo + { + internal Point Point; + + internal XPen LineFormat; + internal XBrush FillFormat; + } + + /// + /// Represents one sector of a series used by a pie chart. + /// + internal class SectorRendererInfo : PointRendererInfo + { + internal XRect Rect; + internal double StartAngle; + internal double SweepAngle; + } + + /// + /// Represents one data point of a series and the corresponding rectangle. + /// + internal class ColumnRendererInfo : PointRendererInfo + { + internal XRect Rect; + } + + /// + /// Stores rendering specific information for one data label entry. + /// + internal class DataLabelEntryRendererInfo : AreaRendererInfo + { + internal string Text; + } + + /// + /// Stores data label specific rendering information. + /// + internal class DataLabelRendererInfo : RendererInfo + { + internal DataLabelEntryRendererInfo[] Entries; + + internal string Format; + internal XFont Font; + internal XBrush FontColor; + internal DataLabelPosition Position; + internal DataLabelType Type; + } + + /// + /// SeriesRendererInfo holds all data series specific rendering information. + /// + internal class SeriesRendererInfo : RendererInfo + { + internal Series _series; + + internal DataLabelRendererInfo _dataLabelRendererInfo; + internal PointRendererInfo[] _pointRendererInfos; + + internal XPen LineFormat; + internal XBrush FillFormat; + + // Used if ChartType is set to Line + internal MarkerRendererInfo _markerRendererInfo; + + /// + /// Gets the sum of all points in PointRendererInfo. + /// + internal double SumOfPoints + { + get + { + double sum = 0; + foreach (PointRendererInfo pri in _pointRendererInfos) + { + if (!double.IsNaN(pri.Point._value)) + sum += Math.Abs(pri.Point._value); + } + return sum; + } + } + } + + /// + /// Represents a description of a marker for a line chart. + /// + internal class MarkerRendererInfo : RendererInfo + { + internal XUnit MarkerSize; + internal MarkerStyle MarkerStyle; + internal XColor MarkerForegroundColor; + internal XColor MarkerBackgroundColor; + } + + /// + /// An AxisRendererInfo holds all axis specific rendering information. + /// + internal class AxisRendererInfo : AreaRendererInfo + { + internal Axis _axis; + + internal double MinimumScale; + internal double MaximumScale; + internal double MajorTick; + internal double MinorTick; + internal TickMarkType MinorTickMark; + internal TickMarkType MajorTickMark; + internal double MajorTickMarkWidth; + internal double MinorTickMarkWidth; + internal XPen MajorTickMarkLineFormat; + internal XPen MinorTickMarkLineFormat; + + //Gridlines + internal XPen MajorGridlinesLineFormat; + internal XPen MinorGridlinesLineFormat; + + //AxisTitle + internal AxisTitleRendererInfo _axisTitleRendererInfo; + + //TickLabels + internal string TickLabelsFormat; + internal XFont TickLabelsFont; + internal XBrush TickLabelsBrush; + internal double TickLabelsHeight; + + //LineFormat + internal XPen LineFormat; + + //Chart.XValues, used for X axis only. + internal XValues XValues; + + /// + /// Sets the x coordinate of the inner rectangle. + /// + internal override double X + { + set + { + base.X = value; + InnerRect.X = value; + } + } + + /// + /// Sets the y coordinate of the inner rectangle. + /// + internal override double Y + { + set + { + base.Y = value; + InnerRect.Y = value + LabelSize.Height / 2; + } + } + + /// + /// Sets the height of the inner rectangle. + /// + internal override double Height + { + set + { + base.Height = value; + InnerRect.Height = value - (InnerRect.Y - Y); + } + } + + /// + /// Sets the width of the inner rectangle. + /// + internal override double Width + { + set + { + base.Width = value; + InnerRect.Width = value - LabelSize.Width / 2; + } + } + internal XRect InnerRect; + internal XSize LabelSize; + } + + internal class AxisTitleRendererInfo : AreaRendererInfo + { + internal AxisTitle _axisTitle; + + internal string AxisTitleText; + internal XFont AxisTitleFont; + internal XBrush AxisTitleBrush; + internal double AxisTitleOrientation; + internal HorizontalAlignment AxisTitleAlignment; + internal VerticalAlignment AxisTitleVerticalAlignment; + internal XSize AxisTitleSize; + } + + /// + /// Represents one description of a legend entry. + /// + internal class LegendEntryRendererInfo : AreaRendererInfo + { + internal SeriesRendererInfo _seriesRendererInfo; + internal LegendRendererInfo _legendRendererInfo; + + internal string EntryText; + + /// + /// Size for the marker only. + /// + internal XSize MarkerSize; + internal XPen MarkerPen; + internal XBrush MarkerBrush; + + /// + /// Width for marker area. Extra spacing for line charts are considered. + /// + internal XSize MarkerArea; + + /// + /// Size for text area. + /// + internal XSize TextSize; + } + + /// + /// Stores legend specific rendering information. + /// + internal class LegendRendererInfo : AreaRendererInfo + { + internal Legend _legend; + + internal XFont Font; + internal XBrush FontColor; + internal XPen BorderPen; + internal LegendEntryRendererInfo[] Entries; + } + + /// + /// Stores rendering information common to all plot area renderers. + /// + internal class PlotAreaRendererInfo : AreaRendererInfo + { + internal PlotArea _plotArea; + + /// + /// Saves the plot area's matrix. + /// + internal XMatrix _matrix; + internal XPen LineFormat; + internal XBrush FillFormat; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererParameters.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererParameters.cs new file mode 100644 index 00000000..2488b609 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/RendererParameters.cs @@ -0,0 +1,105 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the necessary data for chart rendering. + /// + internal class RendererParameters + { + /// + /// Initializes a new instance of the RendererParameters class. + /// + public RendererParameters() + { } + + /// + /// Initializes a new instance of the RendererParameters class with the specified graphics and + /// coordinates. + /// + public RendererParameters(XGraphics gfx, double x, double y, double width, double height) + { + _gfx = gfx; + _box = new XRect(x, y, width, height); + } + + /// + /// Initializes a new instance of the RendererParameters class with the specified graphics and + /// rectangle. + /// + public RendererParameters(XGraphics gfx, XRect boundingBox) + { + _gfx = gfx; + _box = boundingBox; + } + + /// + /// Gets or sets the graphics object. + /// + public XGraphics Graphics + { + get { return _gfx; } + set { _gfx = value; } + } + XGraphics _gfx; + + /// + /// Gets or sets the item to draw. + /// + public object DrawingItem + { + get { return _item; } + set { _item = value; } + } + object _item; + + /// + /// Gets or sets the rectangle for the drawing item. + /// + public XRect Box + { + get { return _box; } + set { _box = value; } + } + XRect _box; + + /// + /// Gets or sets the RendererInfo. + /// + public RendererInfo RendererInfo + { + get { return _rendererInfo; } + set { _rendererInfo = value; } + } + RendererInfo _rendererInfo; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalStackedYAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalStackedYAxisRenderer.cs new file mode 100644 index 00000000..04049494 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalStackedYAxisRenderer.cs @@ -0,0 +1,84 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a Y axis renderer used for charts of type Column2D or Line. + /// + internal class VerticalStackedYAxisRenderer : VerticalYAxisRenderer + { + /// + /// Initializes a new instance of the VerticalYAxisRenderer class with the + /// specified renderer parameters. + /// + internal VerticalStackedYAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Determines the sum of the smallest and the largest stacked column + /// from all series of the chart. + /// + protected override void CalcYAxis(out double yMin, out double yMax) + { + yMin = double.MaxValue; + yMax = double.MinValue; + + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + int maxPoints = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + maxPoints = Math.Max(maxPoints, sri._series._seriesElements.Count); + + for (int pointIdx = 0; pointIdx < maxPoints; ++pointIdx) + { + double valueSumPos = 0, valueSumNeg = 0; + foreach (SeriesRendererInfo sri in cri.seriesRendererInfos) + { + if (sri._pointRendererInfos.Length <= pointIdx) + break; + + ColumnRendererInfo column = (ColumnRendererInfo)sri._pointRendererInfos[pointIdx]; + if (column.Point != null && !double.IsNaN(column.Point._value)) + { + if (column.Point._value < 0) + valueSumNeg += column.Point._value; + else + valueSumPos += column.Point._value; + } + } + yMin = Math.Min(valueSumNeg, yMin); + yMax = Math.Max(valueSumPos, yMax); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalXAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalXAxisRenderer.cs new file mode 100644 index 00000000..280a6261 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalXAxisRenderer.cs @@ -0,0 +1,296 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents an axis renderer used for charts of type Bar2D. + /// + internal class VerticalXAxisRenderer : XAxisRenderer + { + /// + /// Initializes a new instance of the VerticalXAxisRenderer class with the specified renderer parameters. + /// + internal VerticalXAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns an initialized rendererInfo based on the X axis. + /// + internal override RendererInfo Init() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + + AxisRendererInfo xari = new AxisRendererInfo(); + xari._axis = chart._xAxis; + if (xari._axis != null) + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + + CalculateXAxisValues(xari); + InitXValues(xari); + InitAxisTitle(xari, cri.DefaultFont); + InitTickLabels(xari, cri.DefaultFont); + InitAxisLineFormat(xari); + InitGridlines(xari); + } + return xari; + } + + /// + /// Calculates the space used for the X axis. + /// + internal override void Format() + { + AxisRendererInfo xari = ((ChartRendererInfo)_rendererParms.RendererInfo).xAxisRendererInfo; + if (xari._axis != null) + { + AxisTitleRendererInfo atri = xari._axisTitleRendererInfo; + + // Calculate space used for axis title. + XSize titleSize = new XSize(0, 0); + if (atri != null && atri.AxisTitleText != null && atri.AxisTitleText.Length > 0) + titleSize = _rendererParms.Graphics.MeasureString(atri.AxisTitleText, atri.AxisTitleFont); + + // Calculate space used for tick labels. + XSize size = new XSize(0, 0); + foreach (XSeries xs in xari.XValues) + { + foreach (XValue xv in xs) + { + XSize valueSize = _rendererParms.Graphics.MeasureString(xv._value, xari.TickLabelsFont); + size.Height += valueSize.Height; + size.Width = Math.Max(valueSize.Width, size.Width); + } + } + + // Remember space for later drawing. + if (atri != null) + atri.AxisTitleSize = titleSize; + xari.TickLabelsHeight = size.Height; + xari.Height = size.Height; + xari.Width = titleSize.Width + size.Width + xari.MajorTickMarkWidth; + } + } + + /// + /// Draws the horizontal X axis. + /// + internal override void Draw() + { + XGraphics gfx = _rendererParms.Graphics; + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + AxisRendererInfo xari = cri.xAxisRendererInfo; + + double xMin = xari.MinimumScale; + double xMax = xari.MaximumScale; + double xMajorTick = xari.MajorTick; + double xMinorTick = xari.MinorTick; + double xMaxExtension = xari.MajorTick; + + // Draw tick labels. Each tick label will be aligned centered. + int countTickLabels = (int)xMax; + double tickLabelStep = xari.Height / countTickLabels; + XPoint startPos = new XPoint(xari.X + xari.Width - xari.MajorTickMarkWidth, xari.Y + tickLabelStep / 2); + foreach (XSeries xs in xari.XValues) + { + for (int idx = countTickLabels - 1; idx >= 0; --idx) + { + XValue xv = xs[idx]; + string tickLabel = xv._value; + XSize size = gfx.MeasureString(tickLabel, xari.TickLabelsFont); + gfx.DrawString(tickLabel, xari.TickLabelsFont, xari.TickLabelsBrush, startPos.X - size.Width, startPos.Y + size.Height / 2); + startPos.Y += tickLabelStep; + } + } + + // Draw axis. + // First draw tick marks, second draw axis. + double majorTickMarkStart = 0, majorTickMarkEnd = 0, + minorTickMarkStart = 0, minorTickMarkEnd = 0; + GetTickMarkPos(xari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); + + LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, xari.LineFormat); + XPoint[] points = new XPoint[2]; + + // Minor ticks. + if (xari.MinorTickMark != TickMarkType.None) + { + int countMinorTickMarks = (int)(xMax / xMinorTick); + double minorTickMarkStep = xari.Height / countMinorTickMarks; + startPos.Y = xari.Y; + for (int x = 0; x <= countMinorTickMarks; x++) + { + points[0].X = minorTickMarkStart; + points[0].Y = startPos.Y + minorTickMarkStep * x; + points[1].X = minorTickMarkEnd; + points[1].Y = points[0].Y; + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Major ticks. + if (xari.MajorTickMark != TickMarkType.None) + { + int countMajorTickMarks = (int)(xMax / xMajorTick); + double majorTickMarkStep = xari.Height / countMajorTickMarks; + startPos.Y = xari.Y; + for (int x = 0; x <= countMajorTickMarks; x++) + { + points[0].X = majorTickMarkStart; + points[0].Y = startPos.Y + majorTickMarkStep * x; + points[1].X = majorTickMarkEnd; + points[1].Y = points[0].Y; + lineFormatRenderer.DrawLine(points[0], points[1]); + } + } + + // Axis. + if (xari.LineFormat != null) + { + points[0].X = xari.X + xari.Width; + points[0].Y = xari.Y; + points[1].X = xari.X + xari.Width; + points[1].Y = xari.Y + xari.Height; + if (xari.MajorTickMark != TickMarkType.None) + { + points[0].Y -= xari.LineFormat.Width / 2; + points[1].Y += xari.LineFormat.Width / 2; + } + lineFormatRenderer.DrawLine(points[0], points[1]); + } + + // Draw axis title. + AxisTitleRendererInfo atri = xari._axisTitleRendererInfo; + if (atri != null && atri.AxisTitleText != null && atri.AxisTitleText.Length > 0) + { + XRect rect = new XRect(xari.X, xari.Y + xari.Height / 2, atri.AxisTitleSize.Width, 0); + gfx.DrawString(atri.AxisTitleText, atri.AxisTitleFont, atri.AxisTitleBrush, rect); + } + } + + /// + /// Calculates the X axis describing values like minimum/maximum scale, major/minor tick and + /// major/minor tick mark width. + /// + private void CalculateXAxisValues(AxisRendererInfo rendererInfo) + { + // Calculates the maximum number of data points over all series. + SeriesCollection seriesCollection = ((Chart)rendererInfo._axis._parent)._seriesCollection; + int count = 0; + foreach (Series series in seriesCollection) + count = Math.Max(count, series.Count); + + rendererInfo.MinimumScale = 0; + rendererInfo.MaximumScale = count; // At least 0 + rendererInfo.MajorTick = 1; + rendererInfo.MinorTick = 0.5; + rendererInfo.MajorTickMarkWidth = DefaultMajorTickMarkWidth; + rendererInfo.MinorTickMarkWidth = DefaultMinorTickMarkWidth; + } + + /// + /// Initializes the rendererInfo's xvalues. If not set by the user xvalues will be simply numbers + /// from minimum scale + 1 to maximum scale. + /// + private void InitXValues(AxisRendererInfo rendererInfo) + { + rendererInfo.XValues = ((Chart)rendererInfo._axis._parent)._xValues; + if (rendererInfo.XValues == null) + { + rendererInfo.XValues = new XValues(); + XSeries xs = rendererInfo.XValues.AddXSeries(); + for (double i = rendererInfo.MinimumScale + 1; i <= rendererInfo.MaximumScale; ++i) + xs.Add(i.ToString()); + } + } + + /// + /// Calculates the starting and ending y position for the minor and major tick marks. + /// + private void GetTickMarkPos(AxisRendererInfo rendererInfo, + ref double majorTickMarkStart, ref double majorTickMarkEnd, + ref double minorTickMarkStart, ref double minorTickMarkEnd) + { + double majorTickMarkWidth = rendererInfo.MajorTickMarkWidth; + double minorTickMarkWidth = rendererInfo.MinorTickMarkWidth; + double x = rendererInfo.Rect.X + rendererInfo.Rect.Width; + + switch (rendererInfo.MajorTickMark) + { + case TickMarkType.Inside: + majorTickMarkStart = x; + majorTickMarkEnd = x + majorTickMarkWidth; + break; + + case TickMarkType.Outside: + majorTickMarkStart = x - majorTickMarkWidth; + majorTickMarkEnd = x; + break; + + case TickMarkType.Cross: + majorTickMarkStart = x - majorTickMarkWidth; + majorTickMarkEnd = x + majorTickMarkWidth; + break; + + case TickMarkType.None: + majorTickMarkStart = 0; + majorTickMarkEnd = 0; + break; + } + + switch (rendererInfo.MinorTickMark) + { + case TickMarkType.Inside: + minorTickMarkStart = x; + minorTickMarkEnd = x + minorTickMarkWidth; + break; + + case TickMarkType.Outside: + minorTickMarkStart = x - minorTickMarkWidth; + minorTickMarkEnd = x; + break; + + case TickMarkType.Cross: + minorTickMarkStart = x - minorTickMarkWidth; + minorTickMarkEnd = x + minorTickMarkWidth; + break; + + case TickMarkType.None: + minorTickMarkStart = 0; + minorTickMarkEnd = 0; + break; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalYAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalYAxisRenderer.cs new file mode 100644 index 00000000..86d1713f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/VerticalYAxisRenderer.cs @@ -0,0 +1,331 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a Y axis renderer used for charts of type Column2D or Line. + /// + internal class VerticalYAxisRenderer : YAxisRenderer + { + /// + /// Initializes a new instance of the VerticalYAxisRenderer class with the + /// specified renderer parameters. + /// + internal VerticalYAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns a initialized rendererInfo based on the Y axis. + /// + internal override RendererInfo Init() + { + Chart chart = (Chart)_rendererParms.DrawingItem; + XGraphics gfx = _rendererParms.Graphics; + + AxisRendererInfo yari = new AxisRendererInfo(); + yari._axis = chart._yAxis; + InitScale(yari); + if (yari._axis != null) + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + InitTickLabels(yari, cri.DefaultFont); + InitAxisTitle(yari, cri.DefaultFont); + InitAxisLineFormat(yari); + InitGridlines(yari); + } + return yari; + } + + /// + /// Calculates the space used for the Y axis. + /// + internal override void Format() + { + AxisRendererInfo yari = ((ChartRendererInfo)_rendererParms.RendererInfo).yAxisRendererInfo; + if (yari._axis != null) + { + XGraphics gfx = _rendererParms.Graphics; + + XSize size = new XSize(0, 0); + + // height of all ticklabels + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double yMajorTick = yari.MajorTick; + double lineHeight = Double.MinValue; + XSize labelSize = new XSize(0, 0); + for (double y = yMin; y <= yMax; y += yMajorTick) + { + string str = y.ToString(yari.TickLabelsFormat); + labelSize = gfx.MeasureString(str, yari.TickLabelsFont); + size.Height += labelSize.Height; + size.Width = Math.Max(size.Width, labelSize.Width); + lineHeight = Math.Max(lineHeight, labelSize.Height); + } + + // add space for tickmarks + size.Width += yari.MajorTickMarkWidth * 1.5; + + // Measure axis title + XSize titleSize = new XSize(0, 0); + if (yari._axisTitleRendererInfo != null) + { + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + parms.RendererInfo = yari; + AxisTitleRenderer atr = new AxisTitleRenderer(parms); + atr.Format(); + titleSize.Height = yari._axisTitleRendererInfo.Height; + titleSize.Width = yari._axisTitleRendererInfo.Width; + } + + yari.Height = Math.Max(size.Height, titleSize.Height); + yari.Width = size.Width + titleSize.Width; + + yari.InnerRect = yari.Rect; + yari.InnerRect.Y += yari.TickLabelsFont.Height / 2; + yari.LabelSize = labelSize; + } + } + + /// + /// Draws the vertical Y axis. + /// + internal override void Draw() + { + AxisRendererInfo yari = ((ChartRendererInfo)_rendererParms.RendererInfo).yAxisRendererInfo; + + double yMin = yari.MinimumScale; + double yMax = yari.MaximumScale; + double yMajorTick = yari.MajorTick; + double yMinorTick = yari.MinorTick; + + XMatrix matrix = new XMatrix(); + matrix.TranslatePrepend(-yari.InnerRect.X, yMax); + matrix.Scale(1, yari.InnerRect.Height / (yMax - yMin), XMatrixOrder.Append); + matrix.ScalePrepend(1, -1); // mirror horizontal + matrix.Translate(yari.InnerRect.X, yari.InnerRect.Y, XMatrixOrder.Append); + + // Draw axis. + // First draw tick marks, second draw axis. + double majorTickMarkStart = 0, majorTickMarkEnd = 0, + minorTickMarkStart = 0, minorTickMarkEnd = 0; + GetTickMarkPos(yari, ref majorTickMarkStart, ref majorTickMarkEnd, ref minorTickMarkStart, ref minorTickMarkEnd); + + XGraphics gfx = _rendererParms.Graphics; + LineFormatRenderer lineFormatRenderer = new LineFormatRenderer(gfx, yari.LineFormat); + LineFormatRenderer minorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MinorTickMarkLineFormat); + LineFormatRenderer majorTickMarkLineFormat = new LineFormatRenderer(gfx, yari.MajorTickMarkLineFormat); + XPoint[] points = new XPoint[2]; + + // Draw minor tick marks. + if (yari.MinorTickMark != TickMarkType.None) + { + for (double y = yMin + yMinorTick; y < yMax; y += yMinorTick) + { + points[0].X = minorTickMarkStart; + points[0].Y = y; + points[1].X = minorTickMarkEnd; + points[1].Y = y; + matrix.TransformPoints(points); + minorTickMarkLineFormat.DrawLine(points[0], points[1]); + } + } + + double lineSpace = yari.TickLabelsFont.GetHeight(); // old: yari.TickLabelsFont.GetHeight(gfx); + int cellSpace = yari.TickLabelsFont.FontFamily.GetLineSpacing(yari.TickLabelsFont.Style); + double xHeight = yari.TickLabelsFont.Metrics.XHeight; + + XSize labelSize = new XSize(0, 0); + labelSize.Height = lineSpace * xHeight / cellSpace; + + int countTickLabels = (int)((yMax - yMin) / yMajorTick) + 1; + for (int i = 0; i < countTickLabels; ++i) + { + double y = yMin + yMajorTick * i; + string str = y.ToString(yari.TickLabelsFormat); + + labelSize.Width = gfx.MeasureString(str, yari.TickLabelsFont).Width; + + // Draw major tick marks. + if (yari.MajorTickMark != TickMarkType.None) + { + labelSize.Width += yari.MajorTickMarkWidth * 1.5; + points[0].X = majorTickMarkStart; + points[0].Y = y; + points[1].X = majorTickMarkEnd; + points[1].Y = y; + matrix.TransformPoints(points); + majorTickMarkLineFormat.DrawLine(points[0], points[1]); + } + else + labelSize.Width += SpaceBetweenLabelAndTickmark; + + // Draw label text. + XPoint[] layoutText = new XPoint[1]; + layoutText[0].X = yari.InnerRect.X + yari.InnerRect.Width - labelSize.Width; + layoutText[0].Y = y; + matrix.TransformPoints(layoutText); + layoutText[0].Y += labelSize.Height / 2; // Center text vertically. + gfx.DrawString(str, yari.TickLabelsFont, yari.TickLabelsBrush, layoutText[0]); + } + + // Draw axis. + if (yari.LineFormat != null && yari.LineFormat.Width > 0) + { + points[0].X = yari.InnerRect.X + yari.InnerRect.Width; + points[0].Y = yMin; + points[1].X = yari.InnerRect.X + yari.InnerRect.Width; + points[1].Y = yMax; + matrix.TransformPoints(points); + if (yari.MajorTickMark != TickMarkType.None) + { + // yMax is at the upper side of the axis + points[1].Y -= yari.LineFormat.Width / 2; + points[0].Y += yari.LineFormat.Width / 2; + } + lineFormatRenderer.DrawLine(points[0], points[1]); + } + + // Draw axis title + if (yari._axisTitleRendererInfo != null && yari._axisTitleRendererInfo.AxisTitleText != "") + { + RendererParameters parms = new RendererParameters(); + parms.Graphics = gfx; + parms.RendererInfo = yari; + double width = yari._axisTitleRendererInfo.Width; + yari._axisTitleRendererInfo.Rect = yari.InnerRect; + yari._axisTitleRendererInfo.Width = width; + AxisTitleRenderer atr = new AxisTitleRenderer(parms); + atr.Draw(); + } + } + + /// + /// Calculates all values necessary for scaling the axis like minimum/maximum scale or + /// minor/major tick. + /// + private void InitScale(AxisRendererInfo rendererInfo) + { + double yMin, yMax; + CalcYAxis(out yMin, out yMax); + FineTuneYAxis(rendererInfo, yMin, yMax); + + rendererInfo.MajorTickMarkWidth = DefaultMajorTickMarkWidth; + rendererInfo.MinorTickMarkWidth = DefaultMinorTickMarkWidth; + } + + /// + /// Gets the top and bottom position of the major and minor tick marks depending on the + /// tick mark type. + /// + private void GetTickMarkPos(AxisRendererInfo rendererInfo, + ref double majorTickMarkStart, ref double majorTickMarkEnd, + ref double minorTickMarkStart, ref double minorTickMarkEnd) + { + double majorTickMarkWidth = rendererInfo.MajorTickMarkWidth; + double minorTickMarkWidth = rendererInfo.MinorTickMarkWidth; + XRect rect = rendererInfo.Rect; + + switch (rendererInfo.MajorTickMark) + { + case TickMarkType.Inside: + majorTickMarkStart = rect.X + rect.Width; + majorTickMarkEnd = rect.X + rect.Width + majorTickMarkWidth; + break; + + case TickMarkType.Outside: + majorTickMarkStart = rect.X + rect.Width; + majorTickMarkEnd = rect.X + rect.Width - majorTickMarkWidth; + break; + + case TickMarkType.Cross: + majorTickMarkStart = rect.X + rect.Width - majorTickMarkWidth; + majorTickMarkEnd = rect.X + rect.Width + majorTickMarkWidth; + break; + + //TickMarkType.None: + default: + majorTickMarkStart = 0; + majorTickMarkEnd = 0; + break; + } + + switch (rendererInfo.MinorTickMark) + { + case TickMarkType.Inside: + minorTickMarkStart = rect.X + rect.Width; + minorTickMarkEnd = rect.X + rect.Width + minorTickMarkWidth; + break; + + case TickMarkType.Outside: + minorTickMarkStart = rect.X + rect.Width; + minorTickMarkEnd = rect.X + rect.Width - minorTickMarkWidth; + break; + + case TickMarkType.Cross: + minorTickMarkStart = rect.X + rect.Width - minorTickMarkWidth; + minorTickMarkEnd = rect.X + rect.Width + minorTickMarkWidth; + break; + + //TickMarkType.None: + default: + minorTickMarkStart = 0; + minorTickMarkEnd = 0; + break; + } + } + + /// + /// Determines the smallest and the largest number from all series of the chart. + /// + protected virtual void CalcYAxis(out double yMin, out double yMax) + { + yMin = double.MaxValue; + yMax = double.MinValue; + + foreach (Series series in ((Chart)_rendererParms.DrawingItem).SeriesCollection) + { + foreach (Point point in series.Elements) + { + if (!double.IsNaN(point._value)) + { + yMin = Math.Min(yMin, point.Value); + yMax = Math.Max(yMax, point.Value); + } + } + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/WallRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/WallRenderer.cs new file mode 100644 index 00000000..df6a57ef --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/WallRenderer.cs @@ -0,0 +1,62 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents a renderer for the plot area background. + /// + internal class WallRenderer : Renderer + { + /// + /// Initializes a new instance of the WallRenderer class with the specified renderer parameters. + /// + internal WallRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Draws the wall. + /// + internal override void Draw() + { + ChartRendererInfo cri = (ChartRendererInfo)_rendererParms.RendererInfo; + if (cri.plotAreaRendererInfo.FillFormat != null) + { + XRect plotAreaBox = cri.plotAreaRendererInfo.Rect; + if (plotAreaBox.IsEmpty) + return; + + _rendererParms.Graphics.DrawRectangle(cri.plotAreaRendererInfo.FillFormat, plotAreaBox); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/XAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/XAxisRenderer.cs new file mode 100644 index 00000000..8ac728ad --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/XAxisRenderer.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base class for all X axis renderer. + /// + internal abstract class XAxisRenderer : AxisRenderer + { + /// + /// Initializes a new instance of the XAxisRenderer class with the specified renderer parameters. + /// + internal XAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Returns the default tick labels format string. + /// + protected override string GetDefaultTickLabelsFormat() + { + return "0"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/YAxisRenderer.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/YAxisRenderer.cs new file mode 100644 index 00000000..85feb5bd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting.Renderers/YAxisRenderer.cs @@ -0,0 +1,130 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting.Renderers +{ + /// + /// Represents the base class for all Y axis renderer. + /// + internal abstract class YAxisRenderer : AxisRenderer + { + /// + /// Initializes a new instance of the YAxisRenderer class with the specified renderer parameters. + /// + internal YAxisRenderer(RendererParameters parms) + : base(parms) + { } + + /// + /// Calculates optimal minimum/maximum scale and minor/major tick based on yMin and yMax. + /// + protected void FineTuneYAxis(AxisRendererInfo rendererInfo, double yMin, double yMax) + { + if (yMin == double.MaxValue && yMax == double.MinValue) + { + // No series data given. + yMin = 0.0f; + yMax = 0.9f; + } + + if (yMin == yMax) + { + if (yMin == 0) + yMax = 0.9f; + else if (yMin < 0) + yMax = 0; + else if (yMin > 0) + yMax = yMin + 1; + } + + // If the ratio between yMax to yMin is more than 1.2, the smallest number will be set too zero. + // It's Excel's behavior. + if (yMin != 0) + { + if (yMin < 0 && yMax < 0) + { + if (yMin / yMax >= 1.2) + yMax = 0; + } + else if (yMax / yMin >= 1.2) + yMin = 0; + } + + double deltaYRaw = yMax - yMin; + + int digits = (int)(Math.Log(deltaYRaw, 10) + 1); + double normed = deltaYRaw / Math.Pow(10, digits) * 10; + + double normedStepWidth = 1; + if (normed < 2) + normedStepWidth = 0.2f; + else if (normed < 5) + normedStepWidth = 0.5f; + + AxisRendererInfo yari = rendererInfo; + double stepWidth = normedStepWidth * Math.Pow(10.0, digits - 1.0); + if (yari._axis == null || double.IsNaN(yari._axis._majorTick)) + yari.MajorTick = stepWidth; + else + yari.MajorTick = yari._axis._majorTick; + + double roundFactor = stepWidth * 0.5; + if (yari._axis == null || double.IsNaN(yari._axis.MinimumScale)) + { + double signumMin = (yMin != 0) ? yMin / Math.Abs(yMin) : 0; + yari.MinimumScale = (int)(Math.Abs((yMin - roundFactor) / stepWidth) - (1 * signumMin)) * stepWidth * signumMin; + } + else + yari.MinimumScale = yari._axis.MinimumScale; + + if (yari._axis == null || double.IsNaN(yari._axis.MaximumScale)) + { + double signumMax = (yMax != 0) ? yMax / Math.Abs(yMax) : 0; + yari.MaximumScale = (int)(Math.Abs((yMax + roundFactor) / stepWidth) + (1 * signumMax)) * stepWidth * signumMax; + } + else + yari.MaximumScale = yari._axis.MaximumScale; + + if (yari._axis == null || double.IsNaN(yari._axis._minorTick)) + yari.MinorTick = yari.MajorTick / 5; + else + yari.MinorTick = yari._axis._minorTick; + } + + /// + /// Returns the default tick labels format string. + /// + protected override string GetDefaultTickLabelsFormat() + { + return "0.0"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Axis.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Axis.cs new file mode 100644 index 00000000..c542e436 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Axis.cs @@ -0,0 +1,232 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if !WINDOWS_PHONE +using System.ComponentModel; +#endif + +namespace PdfSharp.Charting +{ + /// + /// This class represents an axis in a chart. + /// + public class Axis : ChartObject + { + /// + /// Initializes a new instance of the Axis class with the specified parent. + /// + internal Axis(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Axis Clone() + { + return (Axis)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Axis axis = (Axis)base.DeepCopy(); + if (axis._title != null) + { + axis._title = axis._title.Clone(); + axis._title._parent = axis; + } + if (axis._tickLabels != null) + { + axis._tickLabels = axis._tickLabels.Clone(); + axis._tickLabels._parent = axis; + } + if (axis._lineFormat != null) + { + axis._lineFormat = axis._lineFormat.Clone(); + axis._lineFormat._parent = axis; + } + if (axis._majorGridlines != null) + { + axis._majorGridlines = axis._majorGridlines.Clone(); + axis._majorGridlines._parent = axis; + } + if (axis._minorGridlines != null) + { + axis._minorGridlines = axis._minorGridlines.Clone(); + axis._minorGridlines._parent = axis; + } + return axis; + } + #endregion + + #region Properties + /// + /// Gets the title of the axis. + /// + public AxisTitle Title + { + get { return _title ?? (_title = new AxisTitle(this)); } + } + internal AxisTitle _title; + + /// + /// Gets or sets the minimum value of the axis. + /// + public double MinimumScale + { + get { return _minimumScale; } + set { _minimumScale = value; } + } + internal double _minimumScale = double.NaN; + + /// + /// Gets or sets the maximum value of the axis. + /// + public double MaximumScale + { + get { return _maximumScale; } + set { _maximumScale = value; } + } + internal double _maximumScale = double.NaN; + + /// + /// Gets or sets the interval of the primary tick. + /// + public double MajorTick + { + get { return _majorTick; } + set { _majorTick = value; } + } + internal double _majorTick = double.NaN; + + /// + /// Gets or sets the interval of the secondary tick. + /// + public double MinorTick + { + get { return _minorTick; } + set { _minorTick = value; } + } + internal double _minorTick = double.NaN; + + /// + /// Gets or sets the type of the primary tick mark. + /// + public TickMarkType MajorTickMark + { + get { return _majorTickMark; } + set + { + if (!Enum.IsDefined(typeof(TickMarkType), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(TickMarkType)); + _majorTickMark = value; + _majorTickMarkInitialized = true; + } + } + internal TickMarkType _majorTickMark; + internal bool _majorTickMarkInitialized; + + /// + /// Gets or sets the type of the secondary tick mark. + /// + public TickMarkType MinorTickMark + { + get { return _minorTickMark; } + set + { + if (!Enum.IsDefined(typeof(TickMarkType), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(TickMarkType)); + _minorTickMark = value; + _minorTickMarkInitialized = true; + } + } + internal TickMarkType _minorTickMark; + internal bool _minorTickMarkInitialized; + + /// + /// Gets the label of the primary tick. + /// + public TickLabels TickLabels + { + get { return _tickLabels ?? (_tickLabels = new TickLabels(this)); } + } + internal TickLabels _tickLabels; + + /// + /// Gets the format of the axis line. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + + /// + /// Gets the primary gridline object. + /// + public Gridlines MajorGridlines + { + get { return _majorGridlines ?? (_majorGridlines = new Gridlines(this)); } + } + internal Gridlines _majorGridlines; + + /// + /// Gets the secondary gridline object. + /// + public Gridlines MinorGridlines + { + get { return _minorGridlines ?? (_minorGridlines = new Gridlines(this)); } + } + internal Gridlines _minorGridlines; + + /// + /// Gets or sets, whether the axis has a primary gridline object. + /// + public bool HasMajorGridlines + { + get { return _hasMajorGridlines; } + set { _hasMajorGridlines = value; } + } + internal bool _hasMajorGridlines; + + /// + /// Gets or sets, whether the axis has a secondary gridline object. + /// + public bool HasMinorGridlines + { + get { return _hasMinorGridlines; } + set { _hasMinorGridlines = value; } + } + internal bool _hasMinorGridlines; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/AxisTitle.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/AxisTitle.cs new file mode 100644 index 00000000..967321f8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/AxisTitle.cs @@ -0,0 +1,125 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting +{ + /// + /// Represents the title of an axis. + /// + public class AxisTitle : ChartObject + { + /// + /// Initializes a new instance of the AxisTitle class. + /// + public AxisTitle() + { } + + /// + /// Initializes a new instance of the AxisTitle class with the specified parent. + /// + internal AxisTitle(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new AxisTitle Clone() + { + return (AxisTitle)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + AxisTitle axisTitle = (AxisTitle)base.DeepCopy(); + if (axisTitle._font != null) + { + axisTitle._font = axisTitle._font.Clone(); + axisTitle._font._parent = axisTitle; + } + return axisTitle; + } + #endregion + + #region Properties + /// + /// Gets or sets the caption of the title. + /// + public string Caption + { + get { return _caption; } + set { _caption = value; } + } + internal string _caption = String.Empty; + + /// + /// Gets the font of the title. + /// + public Font Font + { + get { return _font ?? (_font = new Font(this)); } + } + internal Font _font; + + /// + /// Gets or sets the orientation of the caption. + /// + public double Orientation + { + get { return _orientation; } + set { _orientation = value; } + } + internal double _orientation; + + /// + /// Gets or sets the horizontal alignment of the caption. + /// + public HorizontalAlignment Alignment + { + get { return _alignment; } + set { _alignment = value; } + } + internal HorizontalAlignment _alignment; + + /// + /// Gets or sets the vertical alignment of the caption. + /// + public VerticalAlignment VerticalAlignment + { + get { return _verticalAlignment; } + set { _verticalAlignment = value; } + } + internal VerticalAlignment _verticalAlignment; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Chart.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Chart.cs new file mode 100644 index 00000000..c7995a52 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Chart.cs @@ -0,0 +1,241 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents charts with different types. + /// + public class Chart : DocumentObject + { + /// + /// Initializes a new instance of the Chart class. + /// + public Chart() + { } + + /// + /// Initializes a new instance of the Chart class with the specified parent. + /// + internal Chart(DocumentObject parent) : base(parent) { } + + /// + /// Initializes a new instance of the Chart class with the specified chart type. + /// + public Chart(ChartType type) + : this() + { + Type = type; + } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Chart Clone() + { + return (Chart)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Chart chart = (Chart)base.DeepCopy(); + if (chart._xAxis != null) + { + chart._xAxis = chart._xAxis.Clone(); + chart._xAxis._parent = chart; + } + if (chart._yAxis != null) + { + chart._yAxis = chart._yAxis.Clone(); + chart._yAxis._parent = chart; + } + if (chart._zAxis != null) + { + chart._zAxis = chart._zAxis.Clone(); + chart._zAxis._parent = chart; + } + if (chart._seriesCollection != null) + { + chart._seriesCollection = chart._seriesCollection.Clone(); + chart._seriesCollection._parent = chart; + } + if (chart._xValues != null) + { + chart._xValues = chart._xValues.Clone(); + chart._xValues._parent = chart; + } + if (chart._plotArea != null) + { + chart._plotArea = chart._plotArea.Clone(); + chart._plotArea._parent = chart; + } + if (chart._dataLabel != null) + { + chart._dataLabel = chart._dataLabel.Clone(); + chart._dataLabel._parent = chart; + } + return chart; + } + + /// + /// Determines the type of the given axis. + /// + internal string CheckAxis(Axis axis) + { + if ((_xAxis != null) && (axis == _xAxis)) + return "xaxis"; + if ((_yAxis != null) && (axis == _yAxis)) + return "yaxis"; + if ((_zAxis != null) && (axis == _zAxis)) + return "zaxis"; + + return ""; + } + #endregion + + #region Properties + /// + /// Gets or sets the base type of the chart. + /// ChartType of the series can be overwritten. + /// + public ChartType Type + { + get { return _type; } + set { _type = value; } + } + internal ChartType _type; + + /// + /// Gets or sets the font for the chart. This will be the default font for all objects which are + /// part of the chart. + /// + public Font Font + { + get { return _font ?? (_font = new Font(this)); } + } + internal Font _font; + + /// + /// Gets the legend of the chart. + /// + public Legend Legend + { + get { return _legend ?? (_legend = new Legend(this)); } + } + internal Legend _legend; + + /// + /// Gets the X-Axis of the Chart. + /// + public Axis XAxis + { + get { return _xAxis ?? (_xAxis = new Axis(this)); } + } + internal Axis _xAxis; + + /// + /// Gets the Y-Axis of the Chart. + /// + public Axis YAxis + { + get { return _yAxis ?? (_yAxis = new Axis(this)); } + } + internal Axis _yAxis; + + /// + /// Gets the Z-Axis of the Chart. + /// + public Axis ZAxis + { + get { return _zAxis ?? (_zAxis = new Axis(this)); } + } + internal Axis _zAxis; + + /// + /// Gets the collection of the data series. + /// + public SeriesCollection SeriesCollection + { + get { return _seriesCollection ?? (_seriesCollection = new SeriesCollection(this)); } + } + internal SeriesCollection _seriesCollection; + + /// + /// Gets the collection of the values written on the X-Axis. + /// + public XValues XValues + { + get { return _xValues ?? (_xValues = new XValues(this)); } + } + internal XValues _xValues; + + /// + /// Gets the plot (drawing) area of the chart. + /// + public PlotArea PlotArea + { + get { return _plotArea ?? (_plotArea = new PlotArea(this)); } + } + internal PlotArea _plotArea; + + /// + /// Gets or sets a value defining how blanks in the data series should be shown. + /// + public BlankType DisplayBlanksAs + { + get { return _displayBlanksAs; } + set { _displayBlanksAs = value; } + } + internal BlankType _displayBlanksAs; + + /// + /// Gets the DataLabel of the chart. + /// + public DataLabel DataLabel + { + get { return _dataLabel ?? (_dataLabel = new DataLabel(this)); } + } + internal DataLabel _dataLabel; + + /// + /// Gets or sets whether the chart has a DataLabel. + /// + public bool HasDataLabel + { + get { return _hasDataLabel; } + set { _hasDataLabel = value; } + } + internal bool _hasDataLabel; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartFrame.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartFrame.cs new file mode 100644 index 00000000..5646f651 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartFrame.cs @@ -0,0 +1,228 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; +using PdfSharp.Drawing; +using PdfSharp.Charting.Renderers; + +namespace PdfSharp.Charting +{ + /// + /// Represents the frame which holds one or more charts. + /// + public class ChartFrame + { + /// + /// Initializes a new instance of the ChartFrame class. + /// + public ChartFrame() + { } + + /// + /// Initializes a new instance of the ChartFrame class with the specified rectangle. + /// + public ChartFrame(XRect rect) + { + _location = rect.Location; + _size = rect.Size; + } + + /// + /// Gets or sets the location of the ChartFrame. + /// + public XPoint Location + { + get { return _location; } + set { _location = value; } + } + XPoint _location; + + /// + /// Gets or sets the size of the ChartFrame. + /// + public XSize Size + { + get { return _size; } + set { _size = value; } + } + XSize _size; + + /// + /// Adds a chart to the ChartFrame. + /// + public void Add(Chart chart) + { + if (_chartList == null) + _chartList = new List(); + _chartList.Add(chart); + } + + /// + /// Draws all charts inside the ChartFrame. + /// + public void Draw(XGraphics gfx) + { + // Draw frame of ChartFrame. First shadow frame. + const int dx = 5; + const int dy = 5; + gfx.DrawRoundedRectangle(XBrushes.Gainsboro, + _location.X + dx, _location.Y + dy, + _size.Width, _size.Height, 20, 20); + + XRect chartRect = new XRect(_location.X, _location.Y, _size.Width, _size.Height); + XLinearGradientBrush brush = new XLinearGradientBrush(chartRect, XColor.FromArgb(0xFFD0DEEF), XColors.White, + XLinearGradientMode.Vertical); + XPen penBorder = new XPen(XColors.SteelBlue, 2.5); + gfx.DrawRoundedRectangle(penBorder, brush, + _location.X, _location.Y, _size.Width, _size.Height, + 15, 15); + + XGraphicsState state = gfx.Save(); + gfx.TranslateTransform(_location.X, _location.Y); + + // Calculate rectangle for all charts. Y-Position will be moved for each chart. + int charts = _chartList.Count; + const uint dxChart = 20; + const uint dyChart = 20; + const uint dyBetweenCharts = 30; + XRect rect = new XRect(dxChart, dyChart, + _size.Width - 2 * dxChart, + (_size.Height - (charts - 1) * dyBetweenCharts - 2 * dyChart) / charts); + + // draw each chart in list + foreach (Chart chart in _chartList) + { + RendererParameters parms = new RendererParameters(gfx, rect); + parms.DrawingItem = chart; + + ChartRenderer renderer = GetChartRenderer(chart, parms); + renderer.Init(); + renderer.Format(); + renderer.Draw(); + + rect.Y += rect.Height + dyBetweenCharts; + } + gfx.Restore(state); + + // // Calculate rectangle for all charts. Y-Position will be moved for each chart. + // int charts = chartList.Count; + // uint dxChart = 0; + // uint dyChart = 0; + // uint dyBetweenCharts = 0; + // XRect rect = new XRect(dxChart, dyChart, + // size.Width - 2 * dxChart, + // (size.Height - (charts - 1) * dyBetweenCharts - 2 * dyChart) / charts); + // + // // draw each chart in list + // foreach (Chart chart in chartList) + // { + // RendererParameters parms = new RendererParameters(gfx, rect); + // parms.DrawingItem = chart; + // + // ChartRenderer renderer = GetChartRenderer(chart, parms); + // renderer.Init(); + // renderer.Format(); + // renderer.Draw(); + // + // rect.Y += rect.Height + dyBetweenCharts; + // } + } + + /// + /// Draws first chart only. + /// + public void DrawChart(XGraphics gfx) + { + XGraphicsState state = gfx.Save(); + gfx.TranslateTransform(_location.X, _location.Y); + + if (_chartList.Count > 0) + { + XRect chartRect = new XRect(0, 0, _size.Width, _size.Height); + Chart chart = (Chart)_chartList[0]; + RendererParameters parms = new RendererParameters(gfx, chartRect); + parms.DrawingItem = chart; + + ChartRenderer renderer = GetChartRenderer(chart, parms); + renderer.Init(); + renderer.Format(); + renderer.Draw(); + } + gfx.Restore(state); + } + + /// + /// Returns the chart renderer appropriate for the chart. + /// + private ChartRenderer GetChartRenderer(Chart chart, RendererParameters parms) + { + ChartType chartType = chart.Type; + bool useCombinationRenderer = false; + foreach (Series series in chart._seriesCollection) + { + if (series._chartType != chartType) + { + useCombinationRenderer = true; + break; + } + } + + if (useCombinationRenderer) + return new CombinationChartRenderer(parms); + + switch (chartType) + { + case ChartType.Line: + return new LineChartRenderer(parms); + + case ChartType.Column2D: + case ChartType.ColumnStacked2D: + return new ColumnChartRenderer(parms); + + case ChartType.Bar2D: + case ChartType.BarStacked2D: + return new BarChartRenderer(parms); + + case ChartType.Area2D: + return new AreaChartRenderer(parms); + + case ChartType.Pie2D: + case ChartType.PieExploded2D: + return new PieChartRenderer(parms); + } + + return null; + } + + /// + /// Holds the charts which will be drawn inside the ChartFrame. + /// + List _chartList; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartObject.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartObject.cs new file mode 100644 index 00000000..9dd4e587 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/ChartObject.cs @@ -0,0 +1,48 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Base class for all chart classes. + /// + public class ChartObject : DocumentObject + { + /// + /// Initializes a new instance of the ChartObject class. + /// + public ChartObject() + { } + + /// + /// Initializes a new instance of the ChartObject class with the specified parent. + /// + internal ChartObject(DocumentObject parent) : base(parent) { } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/DataLabel.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/DataLabel.cs new file mode 100644 index 00000000..8eda7790 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/DataLabel.cs @@ -0,0 +1,134 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if !WINDOWS_PHONE +using System.ComponentModel; +#endif + +namespace PdfSharp.Charting +{ + /// + /// Represents a DataLabel of a Series + /// + public class DataLabel : DocumentObject + { + /// + /// Initializes a new instance of the DataLabel class. + /// + public DataLabel() + { } + + /// + /// Initializes a new instance of the DataLabel class with the specified parent. + /// + internal DataLabel(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new DataLabel Clone() + { + return (DataLabel)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + DataLabel dataLabel = (DataLabel)base.DeepCopy(); + if (dataLabel._font != null) + { + dataLabel._font = dataLabel._font.Clone(); + dataLabel._font._parent = dataLabel; + } + return dataLabel; + } + #endregion + + #region Properties + /// + /// Gets or sets a numeric format string for the DataLabel. + /// + public string Format + { + get { return _format; } + set { _format = value; } + } + internal string _format = String.Empty; + + /// + /// Gets the Font for the DataLabel. + /// + public Font Font + { + get { return _font ?? (_font = new Font(this)); } + } + internal Font _font; + + /// + /// Gets or sets the position of the DataLabel. + /// + public DataLabelPosition Position + { + get { return (DataLabelPosition)_position; } + set + { + if (!Enum.IsDefined(typeof(DataLabelPosition), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataLabelPosition)); + + _position = value; + _positionInitialized = true; + } + } + internal DataLabelPosition _position; + internal bool _positionInitialized; + + /// + /// Gets or sets the type of the DataLabel. + /// + public DataLabelType Type + { + get { return _type; } + set + { + if (!Enum.IsDefined(typeof(DataLabelType), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(DataLabelType)); + + _type = value; + _typeInitialized = true; + } + } + internal DataLabelType _type; + internal bool _typeInitialized; + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObject.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObject.cs new file mode 100644 index 00000000..5b56e368 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObject.cs @@ -0,0 +1,87 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Base class for all chart classes. + /// + public class DocumentObject + { + /// + /// Initializes a new instance of the DocumentObject class. + /// + public DocumentObject() + { } + + /// + /// Initializes a new instance of the DocumentObject class with the specified parent. + /// + public DocumentObject(DocumentObject parent) + { + _parent = parent; + } + + #region Methods + /// + /// Creates a deep copy of the DocumentObject. The parent of the new object is null. + /// + public object Clone() + { + return DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected virtual object DeepCopy() + { + DocumentObject value = (DocumentObject)MemberwiseClone(); + value._parent = null; + return value; + } + #endregion + + #region Properties + /// + /// Gets the parent object. + /// + public DocumentObject Parent + { + get { return _parent; } + } + + /// + /// + /// + /*protected*/ + internal DocumentObject _parent; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObjectCollection.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObjectCollection.cs new file mode 100644 index 00000000..e221860f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/DocumentObjectCollection.cs @@ -0,0 +1,261 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace PdfSharp.Charting +{ + /// + /// Base class of all collections. + /// + public abstract class DocumentObjectCollection : DocumentObject, IList + { + /// + /// Initializes a new instance of the DocumentObjectCollection class. + /// + internal DocumentObjectCollection() + { + _elements = new List(); + } + + /// + /// Initializes a new instance of the DocumentObjectCollection class with the specified parent. + /// + internal DocumentObjectCollection(DocumentObject parent) + : base(parent) + { + _elements = new List(); + } + + /// + /// Gets the element at the specified index. + /// + public virtual DocumentObject this[int index] + { + get { return _elements[index]; } + internal set { _elements[index] = value; } + } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new DocumentObjectCollection Clone() + { + return (DocumentObjectCollection)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + DocumentObjectCollection coll = (DocumentObjectCollection)base.DeepCopy(); + + int count = Count; + coll._elements = new List(count); + for (int index = 0; index < count; ++index) + coll._elements.Add((DocumentObject)this[index].Clone()); + return coll; + } + + /// + /// Copies the Array_List or a portion of it to a one-dimensional array. + /// + public void CopyTo(Array array, int index) + { + throw new NotImplementedException("TODO"); + //elements.CopyTo(array, index); + } + + /// + /// Removes all elements from the collection. + /// + public void Clear() + { + _elements.Clear(); + } + + /// + /// Inserts an element into the collection at the specified position. + /// + public virtual void InsertObject(int index, DocumentObject val) + { + _elements.Insert(index, val); + } + + /// + /// Searches for the specified object and returns the zero-based index of the first occurrence. + /// + public int IndexOf(DocumentObject val) + { + return _elements.IndexOf(val); + } + + /// + /// Removes the element at the specified index. + /// + public void RemoveObjectAt(int index) + { + _elements.RemoveAt(index); + } + + /// + /// Adds the specified document object to the collection. + /// + public virtual void Add(DocumentObject value) + { + if (value != null) + value._parent = this; + _elements.Add(value); + } + #endregion + + #region Properties + /// + /// Gets the number of elements actually contained in the collection. + /// + public int Count + { + get { return _elements.Count; } + } + + /// + /// Gets the first value in the collection, if there is any, otherwise null. + /// + public DocumentObject First + { + get + { + if (Count > 0) + return this[0]; + return null; + } + } + + /// + /// Gets the last element or null, if no such element exists. + /// + public DocumentObject LastObject + { + get + { + int count = _elements.Count; + if (count > 0) + return (DocumentObject)_elements[count - 1]; + return null; + } + } + #endregion + + #region IList + bool IList.IsReadOnly + { + get { return false; } + } + + bool IList.IsFixedSize + { + get { return false; } + } + + object IList.this[int index] + { + get { return _elements[index]; } + set { _elements[index] = (DocumentObject)value; } + } + + void IList.RemoveAt(int index) + { + throw new NotImplementedException("IList.RemoveAt"); + // TODO: Add DocumentObjectCollection.RemoveAt implementation + } + + void IList.Insert(int index, object value) + { + throw new NotImplementedException("IList.Insert"); + // TODO: Add DocumentObjectCollection.Insert implementation + } + + void IList.Remove(object value) + { + throw new NotImplementedException("IList.Remove"); + // TODO: Add DocumentObjectCollection.Remove implementation + } + + bool IList.Contains(object value) + { + throw new NotImplementedException("IList.Contains"); + // TODO: Add DocumentObjectCollection.Contains implementation + //return false; + } + + int System.Collections.IList.IndexOf(object value) + { + throw new NotImplementedException("IList.IndexOf"); + // TODO: Add DocumentObjectCollection.System.Collections.IList.IndexOf implementation + //return 0; + } + + int IList.Add(object value) + { + throw new NotImplementedException("IList.Add"); + // TODO: Add DocumentObjectCollection.Add implementation + //return 0; + } + #endregion + + #region ICollection + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get { return null; } + } + #endregion + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _elements.GetEnumerator(); + } + + List _elements; + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/FillFormat.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/FillFormat.cs new file mode 100644 index 00000000..ca772d2d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/FillFormat.cs @@ -0,0 +1,82 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting +{ + /// + /// Defines the background filling of the shape. + /// + public class FillFormat : DocumentObject + { + /// + /// Initializes a new instance of the FillFormat class. + /// + public FillFormat() + { } + + /// + /// Initializes a new instance of the FillFormat class with the specified parent. + /// + internal FillFormat(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new FillFormat Clone() + { + return (FillFormat)DeepCopy(); + } + #endregion + + #region Properties + /// + /// Gets or sets the color of the filling. + /// + public XColor Color + { + get { return _color; } + set { _color = value; } + } + internal XColor _color = XColor.Empty; + + /// + /// Gets or sets a value indicating whether the background color should be visible. + /// + public bool Visible + { + get { return _visible; } + set { _visible = value; } + } + internal bool _visible; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Font.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Font.cs new file mode 100644 index 00000000..93911e28 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Font.cs @@ -0,0 +1,169 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Charting +{ + /// + /// Font represents the formatting of characters in a paragraph. + /// + public sealed class Font : DocumentObject + { + /// + /// Initializes a new instance of the Font class that can be used as a template. + /// + public Font() + { } + + /// + /// Initializes a new instance of the Font class with the specified parent. + /// + internal Font(DocumentObject parent) + : base(parent) + { } + + /// + /// Initializes a new instance of the Font class with the specified name and size. + /// + public Font(string name, XUnit size) + : this() + { + _name = name; + _size = size; + } + + #region Methods + /// + /// Creates a copy of the Font. + /// + public new Font Clone() + { + return (Font)DeepCopy(); + } + #endregion + + #region Properties + /// + /// Gets or sets the name of the font. + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + internal string _name = String.Empty; + + /// + /// Gets or sets the size of the font. + /// + public XUnit Size + { + get { return _size; } + set { _size = value; } + } + internal XUnit _size; + + /// + /// Gets or sets the bold property. + /// + public bool Bold + { + get { return _bold; } + set { _bold = value; } + } + internal bool _bold; + + /// + /// Gets or sets the italic property. + /// + public bool Italic + { + get { return _italic; } + set { _italic = value; } + } + internal bool _italic; + + /// + /// Gets or sets the underline property. + /// + public Underline Underline + { + get { return _underline; } + set { _underline = value; } + } + internal Underline _underline; + + /// + /// Gets or sets the color property. + /// + public XColor Color + { + get { return _color; } + set { _color = value; } + } + internal XColor _color = XColor.Empty; + + /// + /// Gets or sets the superscript property. + /// + public bool Superscript + { + get { return _superscript; } + set + { + if (_superscript != value) + { + _superscript = value; + _subscript = false; + } + } + } + internal bool _superscript; + + /// + /// Gets or sets the subscript property. + /// + public bool Subscript + { + get { return _subscript; } + set + { + if (_subscript != value) + { + _subscript = value; + _superscript = false; + } + } + } + internal bool _subscript; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Gridlines.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Gridlines.cs new file mode 100644 index 00000000..6cf13358 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Gridlines.cs @@ -0,0 +1,85 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents the gridlines on the axes. + /// + public class Gridlines : ChartObject + { + /// + /// Initializes a new instance of the Gridlines class. + /// + public Gridlines() + { } + + /// + /// Initializes a new instance of the Gridlines class with the specified parent. + /// + internal Gridlines(DocumentObject parent) + : base(parent) + { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Gridlines Clone() + { + return (Gridlines)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Gridlines gridlines = (Gridlines)base.DeepCopy(); + if (gridlines._lineFormat != null) + { + gridlines._lineFormat = gridlines._lineFormat.Clone(); + gridlines._lineFormat._parent = gridlines; + } + return gridlines; + } + #endregion + + #region Properties + /// + /// Gets the line format of the grid. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Legend.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Legend.cs new file mode 100644 index 00000000..1e1fb706 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Legend.cs @@ -0,0 +1,117 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if !WINDOWS_PHONE +using System.ComponentModel; +#endif + +namespace PdfSharp.Charting +{ + /// + /// Represents a legend of a chart. + /// + public class Legend : ChartObject + { + /// + /// Initializes a new instance of the Legend class. + /// + public Legend() + { } + + /// + /// Initializes a new instance of the Legend class with the specified parent. + /// + internal Legend(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Legend Clone() + { + return (Legend)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Legend legend = (Legend)base.DeepCopy(); + if (legend._lineFormat != null) + { + legend._lineFormat = legend._lineFormat.Clone(); + legend._lineFormat._parent = legend; + } + if (legend._font != null) + { + legend._font = legend._font.Clone(); + legend._font._parent = legend; + } + return legend; + } + #endregion + + #region Properties + /// + /// Gets the line format of the legend's border. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + + /// + /// Gets the font of the legend. + /// + public Font Font + { + get { return _font ?? (_font = new Font(this)); } + } + internal Font _font; + + /// + /// Gets or sets the docking type. + /// + public DockingType Docking + { + get { return _docking; } + set + { + if (!Enum.IsDefined(typeof(DockingType), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(DockingType)); + _docking = value; + } + } + internal DockingType _docking; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/LineFormat.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/LineFormat.cs new file mode 100644 index 00000000..7807fc48 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/LineFormat.cs @@ -0,0 +1,112 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting +{ + /// + /// Defines the format of a line in a shape object. + /// + public class LineFormat : DocumentObject + { + /// + /// Initializes a new instance of the LineFormat class. + /// + public LineFormat() + { } + + /// + /// Initializes a new instance of the LineFormat class with the specified parent. + /// + internal LineFormat(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new LineFormat Clone() + { + return (LineFormat)DeepCopy(); + } + #endregion + + #region Properties + /// + /// Gets or sets a value indicating whether the line should be visible. + /// + public bool Visible + { + get { return _visible; } + set { _visible = value; } + } + internal bool _visible; + + /// + /// Gets or sets the width of the line in XUnit. + /// + public XUnit Width + { + get { return _width; } + set { _width = value; } + } + internal XUnit _width; + + /// + /// Gets or sets the color of the line. + /// + public XColor Color + { + get { return _color; } + set { _color = value; } + } + internal XColor _color = XColor.Empty; + + /// + /// Gets or sets the dash style of the line. + /// + public XDashStyle DashStyle + { + get { return _dashStyle; } + set { _dashStyle = value; } + } + internal XDashStyle _dashStyle; + + /// + /// Gets or sets the style of the line. + /// + public LineStyle Style + { + get { return _style; } + set { _style = value; } + } + internal LineStyle _style; + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/PSCSR.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/PSCSR.cs new file mode 100644 index 00000000..96ccf089 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/PSCSR.cs @@ -0,0 +1,48 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// The Pdf-Sharp-Charting-String-Resources. + /// + // ReSharper disable once InconsistentNaming + internal class PSCSR + { + internal static string InvalidChartTypeForCombination(ChartType chartType) + { + return string.Format("ChartType '{0}' not valid for combination of charts.", chartType.ToString()); + } + + internal static string PercentNotSupportedByColumnDataLabel + { + get { return "Column data label cannot be set to 'Percent'"; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/PlotArea.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/PlotArea.cs new file mode 100644 index 00000000..745e3884 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/PlotArea.cs @@ -0,0 +1,139 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Charting +{ + /// + /// Represents the area where the actual chart is drawn. + /// + public class PlotArea : ChartObject + { + /// + /// Initializes a new instance of the PlotArea class. + /// + internal PlotArea() + { } + + /// + /// Initializes a new instance of the PlotArea class with the specified parent. + /// + internal PlotArea(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new PlotArea Clone() + { + return (PlotArea)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + PlotArea plotArea = (PlotArea)base.DeepCopy(); + if (plotArea._lineFormat != null) + { + plotArea._lineFormat = plotArea._lineFormat.Clone(); + plotArea._lineFormat._parent = plotArea; + } + if (plotArea._fillFormat != null) + { + plotArea._fillFormat = plotArea._fillFormat.Clone(); + plotArea._fillFormat._parent = plotArea; + } + return plotArea; + } + #endregion + + #region Properties + /// + /// Gets the line format of the plot area's border. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + + /// + /// Gets the background filling of the plot area. + /// + public FillFormat FillFormat + { + get { return _fillFormat ?? (_fillFormat = new FillFormat(this)); } + } + internal FillFormat _fillFormat; + + /// + /// Gets or sets the left padding of the area. + /// + public XUnit LeftPadding + { + get { return _leftPadding; } + set { _leftPadding = value; } + } + internal XUnit _leftPadding; + + /// + /// Gets or sets the right padding of the area. + /// + public XUnit RightPadding + { + get { return _rightPadding; } + set { _rightPadding = value; } + } + internal XUnit _rightPadding; + + /// + /// Gets or sets the top padding of the area. + /// + public XUnit TopPadding + { + get { return _topPadding; } + set { _topPadding = value; } + } + internal XUnit _topPadding; + + /// + /// Gets or sets the bottom padding of the area. + /// + public XUnit BottomPadding + { + get { return _bottomPadding; } + set { _bottomPadding = value; } + } + internal XUnit _bottomPadding; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Point.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Point.cs new file mode 100644 index 00000000..6abe936c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Point.cs @@ -0,0 +1,121 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents a formatted value on the data series. + /// + public class Point : ChartObject + { + /// + /// Initializes a new instance of the Point class. + /// + internal Point() + { } + + /// + /// Initializes a new instance of the Point class with a real value. + /// + public Point(double value) + : this() + { + Value = value; + } + + /// + /// Initializes a new instance of the Point class with a real value. + /// + public Point(string value) + : this() + { + // = "34.5 23.9" + Value = 0; + } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Point Clone() + { + return (Point)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Point point = (Point)base.DeepCopy(); + if (point._lineFormat != null) + { + point._lineFormat = point._lineFormat.Clone(); + point._lineFormat._parent = point; + } + if (point._fillFormat != null) + { + point._fillFormat = point._fillFormat.Clone(); + point._fillFormat._parent = point; + } + return point; + } + #endregion + + #region Properties + /// + /// Gets the line format of the data point's border. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + + /// + /// Gets the filling format of the data point. + /// + public FillFormat FillFormat + { + get { return _fillFormat ?? (_fillFormat = new FillFormat(this)); } + } + internal FillFormat _fillFormat; + + /// + /// The actual value of the data point. + /// + public double Value + { + get { return _value; } + set { _value = value; } + } + internal double _value; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/Series.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/Series.cs new file mode 100644 index 00000000..68f15ef1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/Series.cs @@ -0,0 +1,246 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; +#if !SILVERLIGHT +using System.ComponentModel; +#endif + +namespace PdfSharp.Charting +{ + /// + /// Represents a series of data on the chart. + /// + public class Series : ChartObject + { + /// + /// Initializes a new instance of the Series class. + /// + public Series() + { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new Series Clone() + { + return (Series)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + Series series = (Series)base.DeepCopy(); + if (series._seriesElements != null) + { + series._seriesElements = series._seriesElements.Clone(); + series._seriesElements._parent = series; + } + if (series._lineFormat != null) + { + series._lineFormat = series._lineFormat.Clone(); + series._lineFormat._parent = series; + } + if (series._fillFormat != null) + { + series._fillFormat = series._fillFormat.Clone(); + series._fillFormat._parent = series; + } + if (series._dataLabel != null) + { + series._dataLabel = series._dataLabel.Clone(); + series._dataLabel._parent = series; + } + return series; + } + + /// + /// Adds a blank to the series. + /// + public void AddBlank() + { + Elements.AddBlank(); + } + + /// + /// Adds a real value to the series. + /// + public Point Add(double value) + { + return Elements.Add(value); + } + + /// + /// Adds an array of real values to the series. + /// + public void Add(params double[] values) + { + Elements.Add(values); + } + #endregion + + #region Properties + /// + /// The actual value container of the series. + /// + public SeriesElements Elements + { + get { return _seriesElements ?? (_seriesElements = new SeriesElements(this)); } + } + internal SeriesElements _seriesElements; + + /// + /// Gets or sets the name of the series which will be used in the legend. + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + internal string _name = String.Empty; + + /// + /// Gets the line format of the border of each data. + /// + public LineFormat LineFormat + { + get { return _lineFormat ?? (_lineFormat = new LineFormat(this)); } + } + internal LineFormat _lineFormat; + + /// + /// Gets the background filling of the data. + /// + public FillFormat FillFormat + { + get { return _fillFormat ?? (_fillFormat = new FillFormat(this)); } + } + internal FillFormat _fillFormat; + + /// + /// Gets or sets the size of the marker in a line chart. + /// + public XUnit MarkerSize + { + get { return _markerSize; } + set { _markerSize = value; } + } + internal XUnit _markerSize; + + /// + /// Gets or sets the style of the marker in a line chart. + /// + public MarkerStyle MarkerStyle + { + get { return _markerStyle; } + set + { + if (!Enum.IsDefined(typeof(MarkerStyle), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(MarkerStyle)); + + _markerStyle = value; + _markerStyleInitialized = true; + } + } + internal MarkerStyle _markerStyle; + internal bool _markerStyleInitialized; + + /// + /// Gets or sets the foreground color of the marker in a line chart. + /// + public XColor MarkerForegroundColor + { + get { return _markerForegroundColor; } + set { _markerForegroundColor = value; } + } + internal XColor _markerForegroundColor = XColor.Empty; + + /// + /// Gets or sets the background color of the marker in a line chart. + /// + public XColor MarkerBackgroundColor + { + get { return _markerBackgroundColor; } + set { _markerBackgroundColor = value; } + } + internal XColor _markerBackgroundColor = XColor.Empty; + + /// + /// Gets or sets the chart type of the series if it's intended to be different than the + /// global chart type. + /// + public ChartType ChartType + { + get { return _chartType; } + set + { + if (!Enum.IsDefined(typeof(ChartType), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(ChartType)); + + _chartType = value; + } + } + internal ChartType _chartType; + + /// + /// Gets the DataLabel of the series. + /// + public DataLabel DataLabel + { + get { return _dataLabel ?? (_dataLabel = new DataLabel(this)); } + } + internal DataLabel _dataLabel; + + /// + /// Gets or sets whether the series has a DataLabel. + /// + public bool HasDataLabel + { + get { return _hasDataLabel; } + set { _hasDataLabel = value; } + } + internal bool _hasDataLabel; + + /// + /// Gets the element count of the series. + /// + public int Count + { + get + { + return _seriesElements != null ? _seriesElements.Count : 0; + } + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesCollection.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesCollection.cs new file mode 100644 index 00000000..7b0f662f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesCollection.cs @@ -0,0 +1,78 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// The collection of data series. + /// + public class SeriesCollection : DocumentObjectCollection + { + /// + /// Initializes a new instance of the SeriesCollection class. + /// + internal SeriesCollection() + { } + + /// + /// Initializes a new instance of the SeriesCollection class with the specified parent. + /// + internal SeriesCollection(DocumentObject parent) : base(parent) { } + + /// + /// Gets a series by its index. + /// + public new Series this[int index] + { + get { return base[index] as Series; } + } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new SeriesCollection Clone() + { + return (SeriesCollection)DeepCopy(); + } + + /// + /// Adds a new series to the collection. + /// + public Series AddSeries() + { + Series series = new Series(); + // Initialize chart type for each new series. + series._chartType = ((Chart)_parent)._type; + Add(series); + return series; + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesElements.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesElements.cs new file mode 100644 index 00000000..75b36c05 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/SeriesElements.cs @@ -0,0 +1,93 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents the collection of the values in a data series. + /// + public class SeriesElements : DocumentObjectCollection + { + /// + /// Initializes a new instance of the SeriesElements class. + /// + internal SeriesElements() + { } + + /// + /// Initializes a new instance of the SeriesElements class with the specified parent. + /// + internal SeriesElements(DocumentObject parent) : base(parent) { } + + /// + /// Gets a point by its index. + /// + public new Point this[int index] + { + get { return (Point)base[index]; } + } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new SeriesElements Clone() + { + return (SeriesElements)DeepCopy(); + } + + /// + /// Adds a blank to the series. + /// + public void AddBlank() + { + base.Add(null); + } + + /// + /// Adds a new point with a real value to the series. + /// + public Point Add(double value) + { + Point point = new Point(value); + Add(point); + return point; + } + + /// + /// Adds an array of new points with real values to the series. + /// + public void Add(params double[] values) + { + foreach (double val in values) + Add(val); + } + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/TickLabels.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/TickLabels.cs new file mode 100644 index 00000000..e1ccb3a9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/TickLabels.cs @@ -0,0 +1,95 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting +{ + /// + /// Represents the format of the label of each value on the axis. + /// + public class TickLabels : ChartObject + { + /// + /// Initializes a new instance of the TickLabels class. + /// + public TickLabels() + { } + + /// + /// Initializes a new instance of the TickLabels class with the specified parent. + /// + internal TickLabels(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new TickLabels Clone() + { + return (TickLabels)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + TickLabels tickLabels = (TickLabels)base.DeepCopy(); + if (tickLabels._font != null) + { + tickLabels._font = tickLabels._font.Clone(); + tickLabels._font._parent = tickLabels; + } + return tickLabels; + } + #endregion + + #region Properties + /// + /// Gets or sets the label's number format. + /// + public string Format + { + get { return _format; } + set { _format = value; } + } + internal string _format = String.Empty; + + /// + /// Gets the font of the label. + /// + public Font Font + { + get { return _font ?? (_font = new Font(this)); } + } + internal Font _font; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeries.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeries.cs new file mode 100644 index 00000000..bfe4b0a0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeries.cs @@ -0,0 +1,127 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections; + +namespace PdfSharp.Charting +{ + /// + /// Represents a series of data on the X-Axis. + /// + public class XSeries : ChartObject + { + /// + /// Initializes a new instance of the XSeries class. + /// + public XSeries() + { + _xSeriesElements = new XSeriesElements(); + } + + /// + /// Gets the xvalue at the specified index. + /// + public XValue this[int index] + { + get { return (XValue)_xSeriesElements[index]; } + } + + /// + /// The actual value container of the XSeries. + /// + protected XSeriesElements _xSeriesElements; + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new XSeries Clone() + { + return (XSeries)DeepCopy(); + } + + /// + /// Implements the deep copy of the object. + /// + protected override object DeepCopy() + { + XSeries xSeries = (XSeries)base.DeepCopy(); + if (xSeries._xSeriesElements != null) + { + xSeries._xSeriesElements = xSeries._xSeriesElements.Clone(); + xSeries._xSeriesElements._parent = xSeries; + } + return xSeries; + } + + /// + /// Adds a blank to the XSeries. + /// + public void AddBlank() + { + _xSeriesElements.AddBlank(); + } + + /// + /// Adds a value to the XSeries. + /// + public XValue Add(string value) + { + return _xSeriesElements.Add(value); + } + + /// + /// Adds an array of values to the XSeries. + /// + public void Add(params string[] values) + { + _xSeriesElements.Add(values); + } + + /// + /// Gets the enumerator. + /// + /// + public IEnumerator GetEnumerator() + { + return _xSeriesElements.GetEnumerator(); + } + #endregion + + #region Properties + /// + /// Gets the number of xvalues actually contained in the xseries. + /// + public int Count + { + get { return _xSeriesElements.Count; } + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeriesElements.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeriesElements.cs new file mode 100644 index 00000000..5966db9d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/XSeriesElements.cs @@ -0,0 +1,80 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents the collection of the value in an XSeries. + /// + public class XSeriesElements : DocumentObjectCollection + { + /// + /// Initializes a new instance of the XSeriesElements class. + /// + public XSeriesElements() + { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new XSeriesElements Clone() + { + return (XSeriesElements)base.DeepCopy(); + } + + /// + /// Adds a blank to the XSeries. + /// + public void AddBlank() + { + base.Add(null); + } + + /// + /// Adds a value to the XSeries. + /// + public XValue Add(string value) + { + XValue xValue = new XValue(value); + Add(xValue); + return xValue; + } + + /// + /// Adds an array of values to the XSeries. + /// + public void Add(params string[] values) + { + foreach (string val in values) + Add(val); + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/XValue.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/XValue.cs new file mode 100644 index 00000000..7ee307ce --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/XValue.cs @@ -0,0 +1,72 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting +{ + /// + /// Represents the actual value on the XSeries. + /// + public class XValue : ChartObject + { + /// + /// Initializes a new instance of the XValue class. + /// + internal XValue() + { } + + /// + /// Initializes a new instance of the XValue class with the specified value. + /// + public XValue(string value) + : this() + { + if (value == null) + throw new ArgumentNullException("value"); + + _value = value; + } + + /// + /// The actual value of the XValue. + /// + internal string _value; + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new XValue Clone() + { + return (XValue)DeepCopy(); + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/XValues.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/XValues.cs new file mode 100644 index 00000000..a7620441 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/XValues.cs @@ -0,0 +1,76 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Represents the collection of values on the X-Axis. + /// + public class XValues : DocumentObjectCollection + { + /// + /// Initializes a new instance of the XValues class. + /// + public XValues() + { } + + /// + /// Initializes a new instance of the XValues class with the specified parent. + /// + internal XValues(DocumentObject parent) : base(parent) { } + + #region Methods + /// + /// Creates a deep copy of this object. + /// + public new XValues Clone() + { + return (XValues)DeepCopy(); + } + + /// + /// Gets an XSeries by its index. + /// + public new XSeries this[int index] + { + get { return base[index] as XSeries; } + } + + /// + /// Adds a new XSeries to the collection. + /// + public XSeries AddXSeries() + { + XSeries xSeries = new XSeries(); + Add(xSeries); + return xSeries; + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/BlankType.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/BlankType.cs new file mode 100644 index 00000000..c3f8e067 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/BlankType.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Determines how null values will be handled in a chart. + /// + public enum BlankType + { + /// + /// Null value is not plotted. + /// + NotPlotted, + + /// + /// Null value will be interpolated. + /// + Interpolated, + + /// + /// Null value will be handled as zero. + /// + Zero + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/ChartType.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/ChartType.cs new file mode 100644 index 00000000..4145b1d5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/ChartType.cs @@ -0,0 +1,77 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Specifies with type of chart will be drawn. + /// + public enum ChartType + { + /// + /// A line chart. + /// + Line, + + /// + /// A clustered 2d column chart. + /// + Column2D, + + /// + /// A stacked 2d column chart. + /// + ColumnStacked2D, + + /// + /// A 2d area chart. + /// + Area2D, + + /// + /// A clustered 2d bar chart. + /// + Bar2D, + + /// + /// A stacked 2d bar chart. + /// + BarStacked2D, + + /// + /// A 2d pie chart. + /// + Pie2D, + + /// + /// An exploded 2d pie chart. + /// + PieExploded2D, + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelPosition.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelPosition.cs new file mode 100644 index 00000000..1cfae91e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelPosition.cs @@ -0,0 +1,57 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Determines where the data label will be positioned. + /// + public enum DataLabelPosition + { + /// + /// DataLabel will be centered inside the bar or pie. + /// + Center, + + /// + /// Inside the bar or pie at the origin. + /// + InsideBase, + + /// + /// Inside the bar or pie at the edge. + /// + InsideEnd, + + /// + /// Outside the bar or pie. + /// + OutsideEnd + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelType.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelType.cs new file mode 100644 index 00000000..d7e344ec --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DataLabelType.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Determines the type of the data label. + /// + public enum DataLabelType + { + /// + /// No DataLabel. + /// + None, + + /// + /// Percentage of the data. For pie charts only. + /// + Percent, + + /// + /// Value of the data. + /// + Value + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DockingType.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DockingType.cs new file mode 100644 index 00000000..5e6e543e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/DockingType.cs @@ -0,0 +1,54 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Specifies the legend's position inside the chart. + /// + public enum DockingType + { + /// + /// Above the chart. + /// + Top, + /// + /// Below the chart. + /// + Bottom, + /// + /// Left from the chart. + /// + Left, + /// + /// Right from the chart. + /// + Right + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/FontProperties.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/FontProperties.cs new file mode 100644 index 00000000..fa254057 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/FontProperties.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Charting +{ + /// + /// Specifies the properties for the font. + /// FOR INTERNAL USE ONLY. + /// + [Flags] + enum FontProperties + { + None = 0x0000, + Name = 0x0001, + Size = 0x0002, + Bold = 0x0004, + Italic = 0x0008, + Underline = 0x0010, + Color = 0x0020, + Border = 0x0040, + Superscript = 0x0080, + Subscript = 0x0100, + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/HorizontalAlignment.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/HorizontalAlignment.cs new file mode 100644 index 00000000..0c74b062 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/HorizontalAlignment.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Used to determine the horizontal alignment of the axis title. + /// + public enum HorizontalAlignment + { + /// + /// Axis title will be left aligned. + /// + Left, + + /// + /// Axis title will be right aligned. + /// + Right, + + /// + /// Axis title will be centered. + /// + Center + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/LineStyle.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/LineStyle.cs new file mode 100644 index 00000000..c7e2ec71 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/LineStyle.cs @@ -0,0 +1,42 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Specifies the line style of the LineFormat object. + /// + public enum LineStyle + { + /// + /// + /// + Single + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/MarkerStyle.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/MarkerStyle.cs new file mode 100644 index 00000000..fed4f411 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/MarkerStyle.cs @@ -0,0 +1,78 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Symbols of a data point in a line chart. + /// + public enum MarkerStyle + { + /// + /// + /// + None, + /// + /// + /// + Circle, + /// + /// + /// + Dash, + /// + /// + /// + Diamond, + /// + /// + /// + Dot, + /// + /// + /// + Plus, + /// + /// + /// + Square, + /// + /// + /// + Star, + /// + /// + /// + Triangle, + /// + /// + /// + X + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/TickMarkType.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/TickMarkType.cs new file mode 100644 index 00000000..f052cc8f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/TickMarkType.cs @@ -0,0 +1,57 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Determines the position where the Tickmarks will be rendered. + /// + public enum TickMarkType + { + /// + /// Tickmarks are not rendered. + /// + None, + + /// + /// Tickmarks are rendered inside the plot area. + /// + Inside, + + /// + /// Tickmarks are rendered outside the plot area. + /// + Outside, + + /// + /// Tickmarks are rendered inside and outside the plot area. + /// + Cross + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/Underline.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/Underline.cs new file mode 100644 index 00000000..8902a62f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/Underline.cs @@ -0,0 +1,66 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Specifies the underline type for the font. + /// + public enum Underline + { + /// + /// + /// + None, + /// + /// + /// + Single, + /// + /// + /// + Words, + /// + /// + /// + Dotted, + /// + /// + /// + Dash, + /// + /// + /// + DotDash, + /// + /// + /// + DotDotDash, + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/VerticalAlignment.cs b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/VerticalAlignment.cs new file mode 100644 index 00000000..6351e188 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Charting/enums/VerticalAlignment.cs @@ -0,0 +1,52 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Charting +{ + /// + /// Used to determine the vertical alignment of the axis title. + /// + public enum VerticalAlignment + { + /// + /// Axis title will be top aligned. + /// + Top, + + /// + /// Axis title will be centered. + /// + Center, + + /// + /// Axis title will be bottom aligned. + /// + Bottom + } +} diff --git a/src/PDFsharp/src/PdfSharp.Charting/PdfSharp.Charting.csproj b/src/PDFsharp/src/PdfSharp.Charting/PdfSharp.Charting.csproj new file mode 100644 index 00000000..c141b9fe --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/PdfSharp.Charting.csproj @@ -0,0 +1,163 @@ + + + + + Debug + AnyCPU + {6F98A822-41B0-4C7A-85A6-E95C1D3E88EF} + Library + Properties + PdfSharp + PdfSharp.Charting + v3.5 + 512 + SAK + SAK + SAK + SAK + + + + true + full + false + bin\Debug\ + TRACE;DEBUG;CORE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE;CORE + prompt + 4 + bin\Release\PdfSharp.Charting.xml + + + true + + + StrongnameKey.snk + + + + + + + + root\VersionInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {5a6055bc-bf86-4fdd-9f62-0109db7a303b} + PdfSharp + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp.Charting/Properties/AssemblyInfo.cs b/src/PDFsharp/src/PdfSharp.Charting/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..136e51f3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Properties/AssemblyInfo.cs @@ -0,0 +1,54 @@ +#region PDFsharp Charting - A .NET charting library based on PDFsharp +// +// Authors: +// Niklas Schneider +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Reflection; +using System.Runtime.InteropServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("PDFsharp Charting")] +[assembly: AssemblyVersion(PdfSharp.ProductVersionInfo.Version)] +[assembly: AssemblyDescription("A .NET charting library based on PDFsharp.")] +[assembly: AssemblyConfiguration(PdfSharp.ProductVersionInfo.Configuration)] +[assembly: AssemblyCompany(PdfSharp.ProductVersionInfo.Company)] +#if DEBUG + [assembly: AssemblyProduct(PdfSharp.ProductVersionInfo.Product + " (Debug Build)")] +#else + [assembly: AssemblyProduct(PdfSharp.ProductVersionInfo.Product)] +#endif +[assembly: AssemblyCopyright(PdfSharp.ProductVersionInfo.Copyright)] +[assembly: AssemblyTrademark(PdfSharp.ProductVersionInfo.Trademark)] +[assembly: AssemblyCulture(PdfSharp.ProductVersionInfo.Culture)] + +[assembly: ComVisible(false)] +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyName("")] diff --git a/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.de.restext b/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.de.restext new file mode 100644 index 00000000..ddbdd0f6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.de.restext @@ -0,0 +1,7 @@ +; PDFsharp string resources (German) +; +; Must be saved as Unicode (UTF-8 with signature) to force resgen.exe to process German umlauts. + +CultureID = Deutsch (de) + +; ----- Charting Messages ------------------------------------------------------------------------- diff --git a/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.restext b/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.restext new file mode 100644 index 00000000..5f53d327 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp.Charting/Resources/Messages.restext @@ -0,0 +1,7 @@ +; PDFsharp string resources (English) +; +; + +CultureID = Neutral + +; ----- Charting Messages ------------------------------------------------------------------------- diff --git a/src/PDFsharp/src/PdfSharp.Charting/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp.Charting/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp.Charting/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp/!JetBrains/Annotations.cs b/src/PDFsharp/src/PdfSharp/!JetBrains/Annotations.cs new file mode 100644 index 00000000..345f1425 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/!JetBrains/Annotations.cs @@ -0,0 +1,695 @@ +/* + * Copyright 2007-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Not yet used in PDFsharp. + +#pragma warning disable 1591 + +// Currently not used. +#if true_ +using System; +using System.ComponentModel; + +namespace JetBrains.Annotations +{ + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// public class Foo + /// { + /// private string str = "my string"; // Warning: Localizable string + /// } + /// + /// + [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] + public sealed class LocalizationRequiredAttribute : Attribute + { + /// + /// Initializes a new instance of the class with + /// set to . + /// + public LocalizationRequiredAttribute() + : this(true) + { } + + /// + /// Initializes a new instance of the class. + /// + /// true if a element should be localized; otherwise, false. + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + /// + /// Gets a value indicating whether a element should be localized. + /// true if a element should be localized; otherwise, false. + /// + [UsedImplicitly] + public bool Required { get; private set; } + + /// + /// Returns whether the value of the given object is equal to the current . + /// + /// The object to test the value equality of. + /// + /// true if the value of the given object is equal to that of the current; otherwise, false. + /// + public override bool Equals(object obj) + { + var attribute = obj as LocalizationRequiredAttribute; + return attribute != null && attribute.Required == Required; + } + + /// + /// Returns the hash code for this instance. + /// + /// A hash code for the current . + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. + /// The format string should be in -like form + /// + /// + /// + /// [StringFormatMethod("message")] + /// public void ShowError(string message, params object[] args) + /// { + /// //Do something + /// } + /// public void Foo() + /// { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + /// + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Initializes new instance of StringFormatMethodAttribute + /// + /// Specifies which parameter of an annotated method should be treated as format-string + public StringFormatMethodAttribute(string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + /// + /// Gets format parameter name + /// + [UsedImplicitly] + public string FormatParameterName { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one of the parameters + /// of the caller function. + /// For example, ReSharper annotates the parameter of . + /// + /// + /// + /// public void Foo(string param) + /// { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// interface + /// and this method is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// + /// public class Foo : INotifyPropertyChanged + /// { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) + /// {} + /// + /// private string _name; + /// public string Name + /// { + /// get { return _name; } + /// set + /// { + /// _name = value; + /// NotifyChanged("LastName"); // Warning + /// } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute(string parameterName) + { + ParameterName = parameterName; + } + + [UsedImplicitly] + public string ParameterName { get; private set; } + } + + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// + /// [CanBeNull] + /// public object Test() + /// { + /// return null; + /// } + /// + /// public void UseTest() + /// { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null + /// + /// + /// + /// [NotNull] + /// public object Foo() + /// { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Delegate | AttributeTargets.Field, AllowMultiple = false, Inherited = true)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, its name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output means that the methos doesn't return normally.
+ /// canbenull annotation is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute with rows separated by semicolon.
+ ///
+ /// + /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // Regular Assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // String.IsNullOrEmpty + /// + /// + /// // A method that returns null if the parameter is null, and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] + /// public bool TryParse(string s, out Person result) + /// + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string fdt) + : this(fdt, false) + { + } + + public ContractAnnotationAttribute([NotNull] string fdt, bool forceFullStates) + { + FDT = fdt; + ForceFullStates = forceFullStates; + } + + public string FDT { get; private set; } + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() should be used instead. + /// However, using '==' or '!=' for comparison with null is always permitted. + /// + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality + /// { + /// } + /// + /// class UsesNoEquality + /// { + /// public void Test() + /// { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// + /// if (ca1 != null) // OK + /// { + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked with + /// the target attribute to implement or inherit specific type or types. + /// + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// public class ComponentAttribute : Attribute + /// {} + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// public class MyComponent : IComponent + /// {} + /// + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + /// + /// Initializes new instance of BaseTypeRequiredAttribute + /// + /// Specifies which types are required + public BaseTypeRequiredAttribute(Type baseType) + { + BaseTypes = new[] { baseType }; + } + + /// + /// Gets enumerations of specified base types + /// + public Type[] BaseTypes { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)] + public sealed class UsedImplicitlyAttribute : Attribute + { + [UsedImplicitly] + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) + { } + + [UsedImplicitly] + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) + { } + + [UsedImplicitly] + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) + { } + + [UsedImplicitly] + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + /// + /// Gets value indicating what is meant to be used + /// + [UsedImplicitly] + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper + /// to not mark symbols marked with such attributes as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public sealed class MeansImplicitUseAttribute : Attribute + { + [UsedImplicitly] + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) + { } + + [UsedImplicitly] + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) + { + } + + [UsedImplicitly] + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) + { } + + [UsedImplicitly] + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + /// + /// Gets value indicating what is meant to be used + /// + [UsedImplicitly] + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + + /// + /// Only entity marked with attribute considered used + /// + Access = 1, + + /// + /// Indicates implicit assignment to a member + /// + Assign = 2, + + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + + /// + /// Indicates implicit instantiation of a type + /// + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked with or + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + + Itself = 1, + + /// + /// Members of entity marked with attribute are considered used + /// + Members = 2, + + /// + /// Entity marked with attribute and all its members considered used + /// + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API which should not be removed and so is treated as used. + /// + [MeansImplicitUse] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + public PublicAPIAttribute(string comment) { } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter, Inherited = true)] + public sealed class InstantHandleAttribute : Attribute { } + + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as + /// + /// + /// + /// [Pure] + /// private int Multiply(int x, int y) + /// { + /// return x*y; + /// } + /// + /// public void Foo() + /// { + /// const int a=2, b=2; + /// Multiply(a, b); // Waring: Return value of pure method is not used + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + [UsedImplicitly] + public PathReferenceAttribute([PathReference] string basePath) + { + BasePath = basePath; + } + + [UsedImplicitly] + public string BasePath { get; private set; } + } + + // ASP.NET MVC attributes + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC action. + /// If applied to a method, the MVC action name is calculated implicitly from the context. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + [UsedImplicitly] + public string AnonymousProperty { get; private set; } + + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC araa. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : PathReferenceAttribute + { + [UsedImplicitly] + public string AnonymousProperty { get; private set; } + + [UsedImplicitly] + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC controller. + /// If applied to a method, the MVC controller name is calculated implicitly from the context. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + [UsedImplicitly] + public string AnonymousProperty { get; private set; } + + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC partial view. + /// If applied to a method, the MVC partial view name is calculated implicitly from the context. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : PathReferenceAttribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling all inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSupressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC view. + /// If applied to a method, the MVC view name is calculated implicitly from the context. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : PathReferenceAttribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) + /// { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + // Razor attributes + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, Inherited = true)] + public sealed class RazorSectionAttribute : Attribute { } +} +#endif \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/!internal/Configuration.cs b/src/PDFsharp/src/PdfSharp/!internal/Configuration.cs new file mode 100644 index 00000000..d07776be --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/!internal/Configuration.cs @@ -0,0 +1,69 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp +{ + /// + /// Floating point formatting. + /// + static class Config + { + public const string SignificantFigures2 = "0.##"; + public const string SignificantFigures3 = "0.###"; + public const string SignificantFigures4 = "0.####"; + public const string SignificantFigures7 = "0.#######"; + public const string SignificantFigures10 = "0.##########"; + public const string SignificantFigures1Plus9 = "0.0#########"; + } + + static class Const + { + /// + /// Factor to convert from degree to radian measure. + /// + public const double Deg2Rad = Math.PI / 180; // = 0.017453292519943295 + + /// + /// Sinus of the angle to turn a regular font to look oblique. Used for italic simulation. + /// + public const double ItalicSkewAngleSinus = 0.34202014332566873304409961468226; // = sin(20°) + + /// + /// Factor of the em size of a regular font to look bold. Used for bold simulation. + /// Value of 2% found in original XPS 1.0 documentation. + /// + public const double BoldEmphasis = 0.02; + + // The κ (kappa) for drawing a circle or an ellipse with four Bézier splines, specifying the distance of the influence point from the starting or end point of a spline. + // Petzold: 4/3 * tan(α / 4) + public const double κ = 0.5522847498307933984022516322796; // := 4/3 * (1 - cos(-π/4)) / sin(π/4)) <=> 4/3 * (sqrt(2) - 1) <=> 4/3 * tan(π/8) + } +} diff --git a/src/PDFsharp/src/PdfSharp/!internal/Directives.cs b/src/PDFsharp/src/PdfSharp/!internal/Directives.cs new file mode 100644 index 00000000..e418f376 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/!internal/Directives.cs @@ -0,0 +1,113 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// +// Documentation of conditional compilation symbols used in PDFsharp. +// Checks correct settings and obsolete conditional compilation symbols. +// + +#if NewViewMatrix // obsolete +#error NewViewMatrix must not be defined anmore. +#endif + +#if MIGRADOC // obsolete +// empira internal only: Some hacks that make PDFsharp behave like PDFlib when used with Asc.RenderContext. +// Applies to MigraDoc 1.2 only. The Open Source MigraDoc lite does not need this define. +#error MIGRADOC must not be defined anmore. +#endif + +#if NET_ZIP // obsolete +// In .NET 2.0 GZipStream is used instead of SharpZipLib +// This does not work as anticipated. +#error Undefine 'NET_ZIP' because it has no effect anymore. +#endif + +#if NET_2_0 // obsolete +#error Undefine 'NET_2_0' because earlier versions are not supported anymore. +#endif + +#if Gdip // obsolete +#error Conditional compilation symbol 'Gdip' was renamed to 'GDI'. +#endif + +#if GdipUseGdiObjects // obsolete +#error Conditional compilation symbol 'GdipUseGdiObjects' was renamed to 'UseGdiObjects'. +#endif + +// Fragmentation of large object heap is a serious issue that must be tackled in the future. +// Update: .NET 4.51 can ultimately defragment LOH. So maybe we can wait and see. +#if UseMemoryStreams +// Use MemoryStream instead of byte[] to avoid large heap problems. +#error Undefine 'UseMemoryStreams' because it has no effect anymore. +#else +// Use byte[] (instead of MemoryStream) to analyse the symptoms of large heap issues. +#endif + +#if GDI && WPF +// PDFsharp based on both System.Drawing and System.Windows classes +// This is for developing and cross testing only +#elif GDI +// PDFsharp based on System.Drawing classes +#if GdipUseGdiObjects +#error Conditional compilation symbol 'GdipUseGdiObjects' was renamed to 'UseGdiObjects'. +#endif + +#if UseGdiObjects +// PDFsharp X graphics classes have implicit cast operators for GDI+ objects. +// Define this to make it easier to use older code with PDFsharp. +// Undefine this to prevent dependencies to GDI+ +#endif + +#elif WPF +// PDFsharp based on Windows Presentation Foundation. +#elif SILVERLIGHT +// PDFsharp based on 'Silverlight'. +#if !WPF +#error 'SILVERLIGHT' must be defined together with 'WPF' +#endif + +#elif WINDOWS_PHONE +// PDFsharp based on 'Windows Phone'. +#if !WPF +#error 'WINDOWS_PHONE' must be defined together with 'WPF'. +#endif +#if !SILVERLIGHT +#error 'WINDOWS_PHONE' must be defined together with 'SILVERLIGHT'. +#endif + +#elif CORE +// PDFsharp independent of any particular .NET library. +#elif NETFX_CORE +// PDFsharp based on 'WinRT'. +#elif UWP +// PDFsharp based on 'Windows Universal Platform'. +#elif DNC10 +#else +#error Either 'CORE', 'GDI', 'WPF', 'SILVERLIGHT', 'WINDOWS_PHONE', or 'NETFX_CORE' must be defined. Or UWP. Or DNC10. +#endif diff --git a/src/PDFsharp/src/PdfSharp/!internal/TargetContext.cs b/src/PDFsharp/src/PdfSharp/!internal/TargetContext.cs new file mode 100644 index 00000000..2975a5c7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/!internal/TargetContext.cs @@ -0,0 +1,45 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; + +namespace PdfSharp.Internal +{ + // In PDFsharp hybrid build both GDI and WPF is defined. + // This is for development and testing only. +#if GDI && WPF + /// + /// Internal switch indicating what context has to be used if both GDI and WPF are defined. + /// + static class TargetContextHelper + { + public static XGraphicTargetContext TargetContext = XGraphicTargetContext.WPF; + } +#endif +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCode.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCode.cs new file mode 100644 index 00000000..b2ac3047 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCode.cs @@ -0,0 +1,170 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Represents the base class of all bar codes. + /// + public abstract class BarCode : CodeBase + { + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + public BarCode(string text, XSize size, CodeDirection direction) + : base(text, size, direction) + { + Text = text; + Size = size; + Direction = direction; + } + + /// + /// Creates a bar code from the specified code type. + /// + public static BarCode FromType(CodeType type, string text, XSize size, CodeDirection direction) + { + switch (type) + { + case CodeType.Code2of5Interleaved: + return new Code2of5Interleaved(text, size, direction); + + case CodeType.Code3of9Standard: + return new Code3of9Standard(text, size, direction); + + default: + throw new InvalidEnumArgumentException("type", (int)type, typeof(CodeType)); + } + } + + /// + /// Creates a bar code from the specified code type. + /// + public static BarCode FromType(CodeType type, string text, XSize size) + { + return FromType(type, text, size, CodeDirection.LeftToRight); + } + + /// + /// Creates a bar code from the specified code type. + /// + public static BarCode FromType(CodeType type, string text) + { + return FromType(type, text, XSize.Empty, CodeDirection.LeftToRight); + } + + /// + /// Creates a bar code from the specified code type. + /// + public static BarCode FromType(CodeType type) + { + return FromType(type, String.Empty, XSize.Empty, CodeDirection.LeftToRight); + } + + /// + /// When overridden in a derived class gets or sets the wide narrow ratio. + /// + public virtual double WideNarrowRatio + { + get { return 0; } + set { } + } + + /// + /// Gets or sets the location of the text next to the bar code. + /// + public TextLocation TextLocation + { + get { return _textLocation; } + set { _textLocation = value; } + } + TextLocation _textLocation; + + /// + /// Gets or sets the length of the data that defines the bar code. + /// + public int DataLength + { + get { return _dataLength; } + set { _dataLength = value; } + } + int _dataLength; + + /// + /// Gets or sets the optional start character. + /// + public char StartChar + { + get { return _startChar; } + set { _startChar = value; } + } + char _startChar; + + /// + /// Gets or sets the optional end character. + /// + public char EndChar + { + get { return _endChar; } + set { _endChar = value; } + } + char _endChar; + + /// + /// Gets or sets a value indicating whether the turbo bit is to be drawn. + /// (A turbo bit is something special to Kern (computer output processing) company (as far as I know)) + /// + public virtual bool TurboBit + { + get { return _turboBit; } + set { _turboBit = value; } + } + bool _turboBit; + + internal virtual void InitRendering(BarCodeRenderInfo info) + { + if (Text == null) + throw new InvalidOperationException(BcgSR.BarCodeNotSet); + + if (Size.IsEmpty) + throw new InvalidOperationException(BcgSR.EmptyBarCodeSize); + } + + /// + /// When defined in a derived class renders the code. + /// + protected internal abstract void Render(XGraphics gfx, XBrush brush, XFont font, XPoint position); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCodeRenderInfo.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCodeRenderInfo.cs new file mode 100644 index 00000000..900dc046 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BarCodeRenderInfo.cs @@ -0,0 +1,54 @@ +// +// PDFsharp - A library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Holds all temporary information needed during rendering. + /// + class BarCodeRenderInfo + { + public BarCodeRenderInfo(XGraphics gfx, XBrush brush, XFont font, XPoint position) + { + Gfx = gfx; + Brush = brush; + Font = font; + Position = position; + } + + public XGraphics Gfx; + public XBrush Brush; + public XFont Font; + public XPoint Position; + public double BarHeight; + public XPoint CurrPos; + public int CurrPosInString; + public double ThinBarWidth; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BcgSR.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BcgSR.cs new file mode 100644 index 00000000..314659a9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/BcgSR.cs @@ -0,0 +1,93 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + // TODO: Mere with PDFsharp strings table + /// + /// String resources for the empira barcode renderer. + /// + internal class BcgSR + { + internal static string Invalid2Of5Code(string code) + { + return string.Format("'{0}' is not a valid code for an interleave 2 of 5 bar code. It can only represent an even number of digits.", code); + } + + internal static string Invalid3Of9Code(string code) + { + return string.Format("'{0}' is not a valid code for a 3 of 9 standard bar code.", code); + } + + internal static string BarCodeNotSet + { + get { return "A text must be set before rendering the bar code."; } + } + + internal static string EmptyBarCodeSize + { + get { return "A non-empty size must be set before rendering the bar code."; } + } + + internal static string Invalid2of5Relation + { + get { return "Value of relation between thick and thin lines on the interleaved 2 of 5 code must be between 2 and 3."; } + } + + internal static string InvalidMarkName(string name) + { + return string.Format("'{0}' is not a valid mark name for this OMR representation.", name); + } + + internal static string OmrAlreadyInitialized + { + get { return "Mark descriptions cannot be set when marks have already been set on OMR."; } + } + + internal static string DataMatrixTooBig + { + get { return "The given data and encoding combination is too big for the matrix size."; } + } + + internal static string DataMatrixNotSupported + { + get { return "Zero sizes, odd sizes and other than ecc200 coded DataMatrix is not supported."; } + } + + internal static string DataMatrixNull + { + get { return "No DataMatrix code is produced."; } + } + + internal static string DataMatrixInvalid(int columns, int rows) + { + return string.Format("'{1}'x'{0}' is an invalid ecc200 DataMatrix size.", columns, rows); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code2of5Interleaved.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code2of5Interleaved.cs new file mode 100644 index 00000000..ba82fdb4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code2of5Interleaved.cs @@ -0,0 +1,193 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Implementation of the Code 2 of 5 bar code. + /// + public class Code2of5Interleaved : ThickThinBarCode + { + /// + /// Initializes a new instance of Interleaved2of5. + /// + public Code2of5Interleaved() + : base("", XSize.Empty, CodeDirection.LeftToRight) + {} + + /// + /// Initializes a new instance of Interleaved2of5. + /// + public Code2of5Interleaved(string code) + : base(code, XSize.Empty, CodeDirection.LeftToRight) + {} + + /// + /// Initializes a new instance of Interleaved2of5. + /// + public Code2of5Interleaved(string code, XSize size) + : base(code, size, CodeDirection.LeftToRight) + {} + + /// + /// Initializes a new instance of Interleaved2of5. + /// + public Code2of5Interleaved(string code, XSize size, CodeDirection direction) + : base(code, size, direction) + {} + + /// + /// Returns an array of size 5 that represents the thick (true) and thin (false) lines or spaces + /// representing the specified digit. + /// + /// The digit to represent. + static bool[] ThickAndThinLines(int digit) + { + return Lines[digit]; + } + static bool[][] Lines = + { + new bool[] {false, false, true, true, false}, + new bool[] {true, false, false, false, true}, + new bool[] {false, true, false, false, true}, + new bool[] {true, true, false, false, false}, + new bool[] {false, false, true, false, true}, + new bool[] {true, false, true, false, false}, + new bool[] {false, true, true, false, false}, + new bool[] {false, false, false, true, true}, + new bool[] {true, false, false, true, false}, + new bool[] {false, true, false, true, false}, + }; + + /// + /// Renders the bar code. + /// + protected internal override void Render(XGraphics gfx, XBrush brush, XFont font, XPoint position) + { + XGraphicsState state = gfx.Save(); + + BarCodeRenderInfo info = new BarCodeRenderInfo(gfx, brush, font, position); + InitRendering(info); + info.CurrPosInString = 0; + //info.CurrPos = info.Center - Size / 2; + info.CurrPos = position - CodeBase.CalcDistance(AnchorType.TopLeft, Anchor, Size); + + if (TurboBit) + RenderTurboBit(info, true); + RenderStart(info); + while (info.CurrPosInString < Text.Length) + RenderNextPair(info); + RenderStop(info); + if (TurboBit) + RenderTurboBit(info, false); + if (TextLocation != TextLocation.None) + RenderText(info); + + gfx.Restore(state); + } + + /// + /// Calculates the thick and thin line widths, + /// taking into account the required rendering size. + /// + internal override void CalcThinBarWidth(BarCodeRenderInfo info) + { + /* + * The total width is the sum of the following parts: + * Starting lines = 4 * thin + * + + * Code Representation = (2 * thick + 3 * thin) * code.Length + * + + * Stopping lines = 1 * thick + 2 * thin + * + * with r = relation ( = thick / thin), this results in + * + * Total width = (6 + r + (2 * r + 3) * text.Length) * thin + */ + double thinLineAmount = 6 + WideNarrowRatio + (2 * WideNarrowRatio + 3) * Text.Length; + info.ThinBarWidth = Size.Width / thinLineAmount; + } + + private void RenderStart(BarCodeRenderInfo info) + { + RenderBar(info, false); + RenderGap(info, false); + RenderBar(info, false); + RenderGap(info, false); + } + + private void RenderStop(BarCodeRenderInfo info) + { + RenderBar(info, true); + RenderGap(info, false); + RenderBar(info, false); + } + + /// + /// Renders the next digit pair as bar code element. + /// + private void RenderNextPair(BarCodeRenderInfo info) + { + int digitForLines = int.Parse(Text[info.CurrPosInString].ToString()); + int digitForGaps = int.Parse(Text[info.CurrPosInString + 1].ToString()); + bool[] linesArray = Lines[digitForLines]; + bool[] gapsArray = Lines[digitForGaps]; + for (int idx = 0; idx < 5; ++idx) + { + RenderBar(info, linesArray[idx]); + RenderGap(info, gapsArray[idx]); + } + info.CurrPosInString += 2; + } + + /// + /// Checks the code to be convertible into an interleaved 2 of 5 bar code. + /// + /// The code to be checked. + protected override void CheckCode(string text) + { +#if true_ + if (text == null) + throw new ArgumentNullException("text"); + + if (text == "") + throw new ArgumentException(BcgSR.Invalid2Of5Code(text)); + + if (text.Length % 2 != 0) + throw new ArgumentException(BcgSR.Invalid2Of5Code(text)); + + foreach (char ch in text) + { + if (!Char.IsDigit(ch)) + throw new ArgumentException(BcgSR.Invalid2Of5Code(text)); + } +#endif + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code3of9Standard.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code3of9Standard.cs new file mode 100644 index 00000000..6cff91de --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/Code3of9Standard.cs @@ -0,0 +1,271 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Imlpementation of the Code 3 of 9 bar code. + /// + // ReSharper disable once InconsistentNaming + public class Code3of9Standard : ThickThinBarCode + { + /// + /// Initializes a new instance of Standard3of9. + /// + public Code3of9Standard() + : base("", XSize.Empty, CodeDirection.LeftToRight) + { } + + /// + /// Initializes a new instance of Standard3of9. + /// + public Code3of9Standard(string code) + : base(code, XSize.Empty, CodeDirection.LeftToRight) + { } + + /// + /// Initializes a new instance of Standard3of9. + /// + public Code3of9Standard(string code, XSize size) + : base(code, size, CodeDirection.LeftToRight) + { } + + /// + /// Initializes a new instance of Standard3of9. + /// + public Code3of9Standard(string code, XSize size, CodeDirection direction) + : base(code, size, direction) + { } + + /// + /// Returns an array of size 9 that represents the thick (true) and thin (false) lines and spaces + /// representing the specified digit. + /// + /// The character to represent. + private static bool[] ThickThinLines(char ch) + { + return Lines["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%*".IndexOf(ch)]; + } + static readonly bool[][] Lines = + { + // '0' + new bool[] {false, false, false, true, true, false, true, false, false}, + // '1' + new bool[] {true, false, false, true, false, false, false, false, true}, + // '2' + new bool[] {false, false, true, true, false, false, false, false, true}, + // '3' + new bool[] {true, false, true, true, false, false, false, false, false}, + // '4' + new bool[] {false, false, false, true, true, false, false, false, true}, + // '5' + new bool[] {true, false, false, true, true, false, false, false, false}, + // '6' + new bool[] {false, false, true, true, true, false, false, false, false}, + // '7' + new bool[] {false, false, false, true, false, false, true, false, true}, + // '8' + new bool[] {true, false, false, true, false, false, true, false, false}, + // '9' + new bool[] {false, false, true, true, false, false, true, false, false}, + // 'A' + new bool[] {true, false, false, false, false, true, false, false, true}, + // 'B' + new bool[] {false, false, true, false, false, true, false, false, true}, + // 'C' + new bool[] {true, false, true, false, false, true, false, false, false}, + // 'D' + new bool[] {false, false, false, false, true, true, false, false, true}, + // 'E' + new bool[] {true, false, false, false, true, true, false, false, false}, + // 'F' + new bool[] {false, false, true, false, true, true, false, false, false}, + // 'G' + new bool[] {false, false, false, false, false, true, true, false, true}, + // 'H' + new bool[] {true, false, false, false, false, true, true, false, false}, + // 'I' + new bool[] {false, false, true, false, false, true, true, false, false}, + // 'J' + new bool[] {false, false, false, false, true, true, true, false, false}, + // 'K' + new bool[] {true, false, false, false, false, false, false, true, true}, + // 'L' + new bool[] {false, false, true, false, false, false, false, true, true}, + // 'M' + new bool[] {true, false, true, false, false, false, false, true, false}, + // 'N' + new bool[] {false, false, false, false, true, false, false, true, true}, + // 'O' + new bool[] {true, false, false, false, true, false, false, true, false}, + // 'P': + new bool[] {false, false, true, false, true, false, false, true, false}, + // 'Q' + new bool[] {false, false, false, false, false, false, true, true, true}, + // 'R' + new bool[] {true, false, false, false, false, false, true, true, false}, + // 'S' + new bool[] {false, false, true, false, false, false, true, true, false}, + // 'T' + new bool[] {false, false, false, false, true, false, true, true, false}, + // 'U' + new bool[] {true, true, false, false, false, false, false, false, true}, + // 'V' + new bool[] {false, true, true, false, false, false, false, false, true}, + // 'W' + new bool[] {true, true, true, false, false, false, false, false, false}, + // 'X' + new bool[] {false, true, false, false, true, false, false, false, true}, + // 'Y' + new bool[] {true, true, false, false, true, false, false, false, false}, + // 'Z' + new bool[] {false, true, true, false, true, false, false, false, false}, + // '-' + new bool[] {false, true, false, false, false, false, true, false, true}, + // '.' + new bool[] {true, true, false, false, false, false, true, false, false}, + // ' ' + new bool[] {false, true, true, false, false, false, true, false, false}, + // '$' + new bool[] {false, true, false, true, false, true, false, false, false}, + // '/' + new bool[] {false, true, false, true, false, false, false, true, false}, + // '+' + new bool[] {false, true, false, false, false, true, false, true, false}, + // '%' + new bool[] {false, false, false, true, false, true, false, true, false}, + // '*' + new bool[] {false, true, false, false, true, false, true, false, false}, + }; + + + /// + /// Calculates the thick and thin line widths, + /// taking into account the required rendering size. + /// + internal override void CalcThinBarWidth(BarCodeRenderInfo info) + { + /* + * The total width is the sum of the following parts: + * Starting lines = 3 * thick + 7 * thin + * + + * Code Representation = (3 * thick + 7 * thin) * code.Length + * + + * Stopping lines = 3 * thick + 6 * thin + * + * with r = relation ( = thick / thin), this results in + * + * Total width = (13 + 6 * r + (3 * r + 7) * code.Length) * thin + */ + double thinLineAmount = 13 + 6 * WideNarrowRatio + (3 * WideNarrowRatio + 7) * Text.Length; + info.ThinBarWidth = Size.Width / thinLineAmount; + } + + /// + /// Checks the code to be convertible into an standard 3 of 9 bar code. + /// + /// The code to be checked. + protected override void CheckCode(string text) + { + if (text == null) + throw new ArgumentNullException("text"); + + if (text.Length == 0) + throw new ArgumentException(BcgSR.Invalid3Of9Code(text)); + + foreach (char ch in text) + { + if ("0123456789ABCDEFGHIJKLMNOP'QRSTUVWXYZ-. $/+%*".IndexOf(ch) < 0) + throw new ArgumentException(BcgSR.Invalid3Of9Code(text)); + } + } + + /// + /// Renders the bar code. + /// + protected internal override void Render(XGraphics gfx, XBrush brush, XFont font, XPoint position) + { + XGraphicsState state = gfx.Save(); + + BarCodeRenderInfo info = new BarCodeRenderInfo(gfx, brush, font, position); + InitRendering(info); + info.CurrPosInString = 0; + //info.CurrPos = Center - Size / 2; + info.CurrPos = position - CalcDistance(AnchorType.TopLeft, Anchor, Size); + + if (TurboBit) + RenderTurboBit(info, true); + RenderStart(info); + while (info.CurrPosInString < Text.Length) + { + RenderNextChar(info); + RenderGap(info, false); + } + RenderStop(info); + if (TurboBit) + RenderTurboBit(info, false); + if (TextLocation != TextLocation.None) + RenderText(info); + + gfx.Restore(state); + } + + private void RenderNextChar(BarCodeRenderInfo info) + { + RenderChar(info, Text[info.CurrPosInString]); + ++info.CurrPosInString; + } + + private void RenderChar(BarCodeRenderInfo info, char ch) + { + bool[] thickThinLines = ThickThinLines(ch); + int idx = 0; + while (idx < 9) + { + RenderBar(info, thickThinLines[idx]); + if (idx < 8) + RenderGap(info, thickThinLines[idx + 1]); + idx += 2; + } + } + + private void RenderStart(BarCodeRenderInfo info) + { + RenderChar(info, '*'); + RenderGap(info, false); + } + + private void RenderStop(BarCodeRenderInfo info) + { + RenderChar(info, '*'); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeBase.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeBase.cs new file mode 100644 index 00000000..b3ac95da --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeBase.cs @@ -0,0 +1,169 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Represents the base class of all codes. + /// + public abstract class CodeBase + { + /// + /// Initializes a new instance of the class. + /// + public CodeBase(string text, XSize size, CodeDirection direction) + { + _text = text; + _size = size; + _direction = direction; + } + + //public static CodeBase FromType(CodeType type, string text, XSize size, CodeDirection direction) + //{ + // switch (type) + // { + // case CodeType.Code2of5Interleaved: + // return new Code2of5Interleaved(text, size, direction); + + // case CodeType.Code3of9Standard: + // return new Code3of9Standard(text, size, direction); + + // default: + // throw new InvalidEnumArgumentException("type", (int)type, typeof(CodeType)); + // } + //} + + //public static CodeBase FromType(CodeType type, string text, XSize size) + //{ + // return FromType(type, text, size, CodeDirection.LeftToRight); + //} + + //public static CodeBase FromType(CodeType type, string text) + //{ + // return FromType(type, text, XSize.Empty, CodeDirection.LeftToRight); + //} + + //public static CodeBase FromType(CodeType type) + //{ + // return FromType(type, String.Empty, XSize.Empty, CodeDirection.LeftToRight); + //} + + /// + /// Gets or sets the size. + /// + public XSize Size + { + get { return _size; } + set { _size = value; } + } + XSize _size; + + /// + /// Gets or sets the text the bar code shall represent. + /// + public string Text + { + get { return _text; } + set + { + CheckCode(value); + _text = value; + } + } + string _text; + + /// + /// Always MiddleCenter. + /// + public AnchorType Anchor + { + get { return _anchor; } + set { _anchor = value; } + } + AnchorType _anchor; + + /// + /// Gets or sets the drawing direction. + /// + public CodeDirection Direction + { + get { return _direction; } + set { _direction = value; } + } + CodeDirection _direction; + + /// + /// When implemented in a derived class, determines whether the specified string can be used as Text + /// for this bar code type. + /// + /// The code string to check. + /// True if the text can be used for the actual barcode. + protected abstract void CheckCode(string text); + + /// + /// Calculates the distance between an old anchor point and a new anchor point. + /// + /// + /// + /// + public static XVector CalcDistance(AnchorType oldType, AnchorType newType, XSize size) + { + if (oldType == newType) + return new XVector(); + + XVector result; + Delta delta = Deltas[(int)oldType, (int)newType]; + result = new XVector(size.Width / 2 * delta.X, size.Height / 2 * delta.Y); + return result; + } + + struct Delta + { + public Delta(int x, int y) + { + X = x; + Y = y; + } + public readonly int X; + public readonly int Y; + } + static readonly Delta[,] Deltas = new Delta[9, 9] + { + { new Delta(0, 0), new Delta(1, 0), new Delta(2, 0), new Delta(0, 1), new Delta(1, 1), new Delta(2, 1), new Delta(0, 2), new Delta(1, 2), new Delta(2, 2) }, + { new Delta(-1, 0), new Delta(0, 0), new Delta(1, 0), new Delta(-1, 1), new Delta(0, 1), new Delta(1, 1), new Delta(-1, 2), new Delta(0, 2), new Delta(1, 2) }, + { new Delta(-2, 0), new Delta(-1, 0), new Delta(0, 0), new Delta(-2, 1), new Delta(-1, 1), new Delta(0, 1), new Delta(-2, 2), new Delta(-1, 2), new Delta(0, 2) }, + { new Delta(0, -1), new Delta(1, -1), new Delta(2, -1), new Delta(0, 0), new Delta(1, 0), new Delta(2, 0), new Delta(0, 1), new Delta(1, 1), new Delta(2, 1) }, + { new Delta(-1, -1), new Delta(0, -1), new Delta(1, -1), new Delta(-1, 0), new Delta(0, 0), new Delta(1, 0), new Delta(-1, 1), new Delta(0, 1), new Delta(1, 1) }, + { new Delta(-2, -1), new Delta(-1, -1), new Delta(0, -1), new Delta(-2, 0), new Delta(-1, 0), new Delta(0, 0), new Delta(-2, 1), new Delta(-1, 1), new Delta(0, 1) }, + { new Delta(0, -2), new Delta(1, -2), new Delta(2, -2), new Delta(0, -1), new Delta(1, -1), new Delta(2, -1), new Delta(0, 0), new Delta(1, 0), new Delta(2, 0) }, + { new Delta(-1, -2), new Delta(0, -2), new Delta(1, -2), new Delta(-1, -1), new Delta(0, -1), new Delta(1, -1), new Delta(-1, 0), new Delta(0, 0), new Delta(1, 0) }, + { new Delta(-2, -2), new Delta(-1, -2), new Delta(0, -2), new Delta(-2, -1), new Delta(-1, -1), new Delta(0, -1), new Delta(-2, 0), new Delta(-1, 0), new Delta(0, 0) }, + }; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeDataMatrix.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeDataMatrix.cs new file mode 100644 index 00000000..09781a1d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeDataMatrix.cs @@ -0,0 +1,217 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// David Stephensen +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Defines the DataMatrix 2D barcode. THIS IS AN EMPIRA INTERNAL IMPLEMENTATION. THE CODE IN + /// THE OPEN SOURCE VERSION IS A FAKE. + /// + public class CodeDataMatrix : MatrixCode + { + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix() + : this("", "", 26, 26, 0, XSize.Empty) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, int length) + : this(code, "", length, length, 0, XSize.Empty) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, int length, XSize size) + : this(code, "", length, length, 0, size) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, DataMatrixEncoding dmEncoding, int length, XSize size) + : this(code, CreateEncoding(dmEncoding, code.Length), length, length, 0, size) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, int rows, int columns) + : this(code, "", rows, columns, 0, XSize.Empty) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, int rows, int columns, XSize size) + : this(code, "", rows, columns, 0, size) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, DataMatrixEncoding dmEncoding, int rows, int columns, XSize size) + : this(code, CreateEncoding(dmEncoding, code.Length), rows, columns, 0, size) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, int rows, int columns, int quietZone) + : this(code, "", rows, columns, quietZone, XSize.Empty) + {} + + /// + /// Initializes a new instance of CodeDataMatrix. + /// + public CodeDataMatrix(string code, string encoding, int rows, int columns, int quietZone, XSize size) + : base(code, encoding, rows, columns, size) + { + QuietZone = quietZone; + } + + /// + /// Sets the encoding of the DataMatrix. + /// + public void SetEncoding(DataMatrixEncoding dmEncoding) + { + Encoding = CreateEncoding(dmEncoding, Text.Length); + } + + static string CreateEncoding(DataMatrixEncoding dmEncoding, int length) + { + string tempencoding = ""; + switch (dmEncoding) + { + case DataMatrixEncoding.Ascii: + tempencoding = new string('a', length); + break; + case DataMatrixEncoding.C40: + tempencoding = new string('c', length); + break; + case DataMatrixEncoding.Text: + tempencoding = new string('t', length); + break; + case DataMatrixEncoding.X12: + tempencoding = new string('x', length); + break; + case DataMatrixEncoding.EDIFACT: + tempencoding = new string('e', length); + break; + case DataMatrixEncoding.Base256: + tempencoding = new string('b', length); + break; + } + return tempencoding; + } + + /// + /// Gets or sets the size of the Matrix' Quiet Zone. + /// + public int QuietZone + { + get { return _quietZone; } + set { _quietZone = value; } + } + int _quietZone; + + /// + /// Renders the matrix code. + /// + protected internal override void Render(XGraphics gfx, XBrush brush, XPoint position) + { + XGraphicsState state = gfx.Save(); + + switch (Direction) + { + case CodeDirection.RightToLeft: + gfx.RotateAtTransform(180, position); + break; + + case CodeDirection.TopToBottom: + gfx.RotateAtTransform(90, position); + break; + + case CodeDirection.BottomToTop: + gfx.RotateAtTransform(-90, position); + break; + } + + XPoint pos = position + CalcDistance(Anchor, AnchorType.TopLeft, Size); + + if (MatrixImage == null) + MatrixImage = DataMatrixImage.GenerateMatrixImage(Text, Encoding, Rows, Columns); + + if (QuietZone > 0) + { + XSize sizeWithZone = new XSize(Size.Width, Size.Height); + sizeWithZone.Width = sizeWithZone.Width / (Columns + 2 * QuietZone) * Columns; + sizeWithZone.Height = sizeWithZone.Height / (Rows + 2 * QuietZone) * Rows; + + XPoint posWithZone = new XPoint(pos.X, pos.Y); + posWithZone.X += Size.Width / (Columns + 2 * QuietZone) * QuietZone; + posWithZone.Y += Size.Height / (Rows + 2 * QuietZone) * QuietZone; + + gfx.DrawRectangle(XBrushes.White, pos.X, pos.Y, Size.Width, Size.Height); + gfx.DrawImage(MatrixImage, posWithZone.X, posWithZone.Y, sizeWithZone.Width, sizeWithZone.Height); + } + else + gfx.DrawImage(MatrixImage, pos.X, pos.Y, Size.Width, Size.Height); + + gfx.Restore(state); + } + + /// + /// Determines whether the specified string can be used as data in the DataMatrix. + /// + /// The code to be checked. + protected override void CheckCode(string text) + { + if (text == null) + throw new ArgumentNullException("text"); + + DataMatrixImage mImage = new DataMatrixImage(Text, Encoding, Rows, Columns); + mImage.Iec16022Ecc200(Columns, Rows, Encoding, Text.Length, Text, 0, 0, 0); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeOmr.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeOmr.cs new file mode 100644 index 00000000..61da4e41 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/CodeOmr.cs @@ -0,0 +1,247 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Represents an OMR code. + /// + public class CodeOmr : BarCode + { + /// + /// initializes a new OmrCode with the given data. + /// + public CodeOmr(string text, XSize size, CodeDirection direction) + : base(text, size, direction) + { } + + /// + /// Renders the OMR code. + /// + protected internal override void Render(XGraphics gfx, XBrush brush, XFont font, XPoint position) + { + XGraphicsState state = gfx.Save(); + + switch (Direction) + { + case CodeDirection.RightToLeft: + gfx.RotateAtTransform(180, position); + break; + + case CodeDirection.TopToBottom: + gfx.RotateAtTransform(90, position); + break; + + case CodeDirection.BottomToTop: + gfx.RotateAtTransform(-90, position); + break; + } + + //XPoint pt = center - size / 2; + XPoint pt = position - CodeBase.CalcDistance(AnchorType.TopLeft, Anchor, Size); + uint value; + uint.TryParse(Text, out value); +#if true + // HACK: Project Wallenwein: set LK + value |= 1; + _synchronizeCode = true; +#endif + if (_synchronizeCode) + { + XRect rect = new XRect(pt.X, pt.Y, _makerThickness, Size.Height); + gfx.DrawRectangle(brush, rect); + pt.X += 2 * _makerDistance; + } + for (int idx = 0; idx < 32; idx++) + { + if ((value & 1) == 1) + { + XRect rect = new XRect(pt.X + idx * _makerDistance, pt.Y, _makerThickness, Size.Height); + gfx.DrawRectangle(brush, rect); + } + value = value >> 1; + } + gfx.Restore(state); + } + + /// + /// Gets or sets a value indicating whether a synchronize mark is rendered. + /// + public bool SynchronizeCode + { + get { return _synchronizeCode; } + set { _synchronizeCode = value; } + } + bool _synchronizeCode; + + /// + /// Gets or sets the distance of the markers. + /// + public double MakerDistance + { + get { return _makerDistance; } + set { _makerDistance = value; } + } + double _makerDistance = 12; // 1/6" + + /// + /// Gets or sets the thickness of the makers. + /// + public double MakerThickness + { + get { return _makerThickness; } + set { _makerThickness = value; } + } + double _makerThickness = 1; + + ///// + ///// Renders the mark at the given position. + ///// + ///// The mark position to render. + //private void RenderMark(int position) + //{ + // double yPos = TopLeft.Y + UpperDistance + position * ToUnit(markDistance).Centimeter; + // //Center mark + // double xPos = TopLeft.X + Width / 2 - this.MarkWidth / 2; + + // Gfx.DrawLine(pen, xPos, yPos, xPos + MarkWidth, yPos); + //} + + ///// + ///// Distance of the marks. Default is 2/6 inch. + ///// + //public MarkDistance MarkDistance + //{ + // get { return markDistance; } + // set { markDistance = value; } + //} + //private MarkDistance markDistance = MarkDistance.Inch2_6; + + ///// + ///// Converts a mark distance to an XUnit object. + ///// + ///// The mark distance to convert. + ///// The converted mark distance. + //public static XUnit ToUnit(MarkDistance markDistance) + //{ + // switch (markDistance) + // { + // case MarkDistance.Inch1_6: + // return XUnit.FromInch(1.0 / 6.0); + // case MarkDistance.Inch2_6: + // return XUnit.FromInch(2.0 / 6.0); + // case MarkDistance.Inch2_8: + // return XUnit.FromInch(2.0 / 8.0); + // default: + // throw new ArgumentOutOfRangeException("markDistance"); + // } + //} + + ///// + ///// The upper left point of the reading zone. + ///// + //public XPoint TopLeft + //{ + // get + // { + // XPoint topLeft = center; + // topLeft.X -= Width; + // double height = upperDistance + lowerDistance; + // height += (data.Marks.Length - 1) * ToUnit(MarkDistance).Centimeter; + // topLeft.Y -= height / 2; + // return topLeft; + // } + //} + + ///// + ///// the upper distance from position to the first mark. + ///// The default value is 8 / 6 inch. + ///// + //double UpperDistance + //{ + // get { return upperDistance; } + // set { upperDistance = value; } + //} + //private double upperDistance = XUnit.FromInch(8.0 / 6.0).Centimeter; + + ///// + ///// The lower distance from the last possible mark to the end of the reading zone. + ///// The default value is + ///// + //double LowerDistance + //{ + // get { return lowerDistance; } + // set { lowerDistance = value; } + //} + //private double lowerDistance = XUnit.FromInch(2.0 / 6.0).Centimeter; + + ///// + ///// Gets or sets the width of the reading zone. + ///// Default and minimum is 3/12 inch. + ///// + //public double Width + //{ + // get { return width; } + // set { width = value; } + //} + //double width = XUnit.FromInch(3.0 / 12.0).Centimeter; + + ///// + ///// Gets or sets the mark width. Default is 1/2 * width. + ///// + //public XUnit MarkWidth + //{ + // get + // { + // if (markWidth > 0) + // return markWidth; + // else + // return width / 2; + // } + // set { markWidth = value; } + //} + //XUnit markWidth; + + ///// + ///// Gets or sets the width of the mark line. Default is 1pt. + ///// + //public XUnit MarkLineWidth + //{ + // get { return markLineWidth; } + // set { markLineWidth = value; } + //} + //XUnit markLineWidth = 1; + + /// + /// Determines whether the specified string can be used as Text for the OMR code. + /// + protected override void CheckCode(string text) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.cs new file mode 100644 index 00000000..0f8eb14d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.cs @@ -0,0 +1,787 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// David Stephensen +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif + +// WPFHACK +#pragma warning disable 162 + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Creates the XImage object for a DataMatrix. + /// + internal class DataMatrixImage + { + public static XImage GenerateMatrixImage(string text, string encoding, int rows, int columns) + { + DataMatrixImage dataMatrixImage = new DataMatrixImage(text, encoding, rows, columns); + return dataMatrixImage.DrawMatrix(); + } + + public DataMatrixImage(string text, string encoding, int rows, int columns) + { + _text = text; + _encoding = encoding; + _rows = rows; + _columns = columns; + } + + string _encoding; + readonly string _text; + readonly int _rows; + readonly int _columns; + + /// + /// Possible ECC200 Matrices. + /// + static Ecc200Block[] ecc200Sizes = + { + new Ecc200Block( 10, 10, 10, 10, 3, 3, 5), // + new Ecc200Block( 12, 12, 12, 12, 5, 5, 7), // + new Ecc200Block( 8, 18, 8, 18, 5, 5, 7), // + new Ecc200Block( 14, 14, 14, 14, 8, 8, 10), // + new Ecc200Block( 8, 32, 8, 16, 10, 10, 11), // + new Ecc200Block( 16, 16, 16, 16, 12, 12, 12), // + new Ecc200Block( 12, 26, 12, 26, 16, 16, 14), // + new Ecc200Block( 18, 18, 18, 18, 18, 18, 14), // + new Ecc200Block( 20, 20, 20, 20, 22, 22, 18), // + new Ecc200Block( 12, 36, 12, 18, 22, 22, 18), // + new Ecc200Block( 22, 22, 22, 22, 30, 30, 20), // Post + new Ecc200Block( 16, 36, 16, 18, 32, 32, 24), // + new Ecc200Block( 24, 24, 24, 24, 36, 36, 24), // + new Ecc200Block( 26, 26, 26, 26, 44, 44, 28), // Post + new Ecc200Block( 16, 48, 16, 24, 49, 49, 28), // + new Ecc200Block( 32, 32, 16, 16, 62, 62, 36), // + new Ecc200Block( 36, 36, 18, 18, 86, 86, 42), // + new Ecc200Block( 40, 40, 20, 20, 114, 114, 48), // + new Ecc200Block( 44, 44, 22, 22, 144, 144, 56), // + new Ecc200Block( 48, 48, 24, 24, 174, 174, 68), // + new Ecc200Block( 52, 52, 26, 26, 204, 102, 42), // + new Ecc200Block( 64, 64, 16, 16, 280, 140, 56), // + new Ecc200Block( 72, 72, 18, 18, 368, 92, 36), // + new Ecc200Block( 80, 80, 20, 20, 456, 114, 48), // + new Ecc200Block( 88, 88, 22, 22, 576, 144, 56), // + new Ecc200Block( 96, 96, 24, 24, 696, 174, 68), // + new Ecc200Block(104, 104, 26, 26, 816, 136, 56), // + new Ecc200Block(120, 120, 20, 20, 1050, 175, 68), // + new Ecc200Block(132, 132, 22, 22, 1304, 163, 62), // + new Ecc200Block(144, 144, 24, 24, 1558, 156, 62), // 156*4+155*2 + new Ecc200Block( 0, 0, 0, 0, 0, 0, 0) // terminate + }; + + public XImage DrawMatrix() + { + return CreateImage(DataMatrix(), _rows, _columns); + } + + /// + /// Creates the DataMatrix code. + /// + internal char[] DataMatrix() + { + int matrixColumns = _columns; + int matrixRows = _rows; + int ecc = 200; + if (String.IsNullOrEmpty(_encoding)) + _encoding = new String('a', _text.Length); + int len = 0; + int maxlen = 0; + int ecclen = 0; + char[] grid = null; + + if (matrixColumns != 0 && matrixRows != 0 && (matrixColumns & 1) != 0 && (matrixRows & 1) != 0 && ecc == 200) + throw new ArgumentException(BcgSR.DataMatrixNotSupported); + + grid = Iec16022Ecc200(matrixColumns, matrixRows, _encoding, _text.Length, _text, len, maxlen, ecclen); + + if (grid == null || matrixColumns == 0) + throw new ArgumentException(BcgSR.DataMatrixNull); //DaSt: ever happen? + return grid; + } + + /// + /// Encodes the DataMatrix. + /// + internal char[] Iec16022Ecc200(int columns, int rows, string encoding, int barcodeLength, string barcode, int len, int max, int ecc) + { + char[] binary = new char[3000]; // encoded raw data and ecc to place in barcode + Ecc200Block matrix = new Ecc200Block(0, 0, 0, 0, 0, 0, 0); + for (int i = 0; i < 3000; i++) + binary[i] = (char)0; + + foreach (Ecc200Block eccmatrix in ecc200Sizes) + { + matrix = eccmatrix; + if (matrix.Width == columns && matrix.Height == rows) + break; + } + + if (matrix.Width == 0) + throw new ArgumentException(BcgSR.DataMatrixInvalid(columns, rows)); + + if (!Ecc200Encode(ref binary, matrix.Bytes, barcode, barcodeLength, encoding, ref len)) + throw new ArgumentException(BcgSR.DataMatrixTooBig); + + // ecc code + Ecc200(binary, matrix.Bytes, matrix.DataBlock, matrix.RSBlock); + // placement + int x; + int y; + int NR; + int[] places; + int NC = columns - 2 * (columns / matrix.CellWidth); + NR = rows - 2 * (rows / matrix.CellHeight); + places = new int[NC * NR]; + Ecc200Placement(ref places, NR, NC); + char[] grid = new char[columns * rows]; + for (y = 0; y < rows; y += matrix.CellHeight) + { + for (x = 0; x < columns; x++) + grid[y * columns + x] = (char)1; + for (x = 0; x < columns; x += 2) + grid[(y + matrix.CellHeight - 1) * columns + x] = (char)1; + } + + for (x = 0; x < columns; x += matrix.CellWidth) + { + for (y = 0; y < rows; y++) + grid[y * columns + x] = (char)1; + for (y = 0; y < rows; y += 2) + grid[y * columns + x + matrix.CellWidth - 1] = (char)1; + } + + for (y = 0; y < NR; y++) + { + for (x = 0; x < NC; x++) + { + int v = places[(NR - y - 1) * NC + x]; + if (v == 1 || v > 7 && ((binary[(v >> 3) - 1] & (1 << (v & 7))) != 0)) + grid[(1 + y + 2 * (y / (matrix.CellHeight - 2))) * columns + 1 + x + 2 * (x / (matrix.CellWidth - 2))] = (char)1; + } + } + return grid; + } + + /// + /// Encodes the barcode with the DataMatrix ECC200 Encoding. + /// + internal bool Ecc200Encode(ref char[] t, int targetLength, string s, int sourceLength, string encoding, ref int len) + { + char enc = 'a'; // start in ASCII encoding mode + int targetposition = 0; + int sourceposition = 0; + if (encoding.Length < sourceLength) + return false; + + // do the encoding + while (sourceposition < sourceLength && targetposition < targetLength) + { + char newenc = enc; // suggest new encoding + if (targetLength - targetposition <= 1 && (enc == 'c' || enc == 't') || targetLength - targetposition <= 2 && enc == 'x') + enc = 'a'; // auto revert to ASCII +#if !SILVERLIGHT + // StL: Who wrote this nonsense? + //newenc = char.Parse(encoding[sourceposition].ToString(CultureInfo.InvariantCulture).ToLower()); + newenc = char.ToLower(encoding[sourceposition]); +#else + throw new NotImplementedException("char.Parse"); +#endif + switch (newenc) + { // encode character + case 'c': // C40 + case 't': // Text + case 'x': // X12 + { + char[] output = new char[6]; + char p = (char)0; + + string e = null; + string s2 = "!\"#$%&'()*+,-./:;<=>?@[\\]_"; + string s3 = null; + if (newenc == 'c') + { + e = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + s3 = "`abcdefghijklmnopqrstuvwxyz{|}~±"; + } + if (newenc == 't') + { + e = " 0123456789abcdefghijklmnopqrstuvwxyz"; + s3 = "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~±"; + } + if (newenc == 'x') + e = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\r*>"; + do + { + char c = s[sourceposition++]; + char w; + if ((c & 0x80) != 0) + { + if (newenc == 'x') + { + // fprintf (stderr, "Cannot encode char 0x%02X in X12\n", c); + return false; + } + c &= (char)0x7f; + output[p++] = (char)1; + output[p++] = (char)30; + } + w = e.IndexOf(c) == -1 ? (char)0 : e[e.IndexOf(c)]; + if (w != (char)0) + output[p++] = (char)((e.IndexOf(w) + 3) % 40); + else + { + if (newenc == 'x') + { + //fprintf (stderr, "Cannot encode char 0x%02X in X12\n", c); + return false; + } + if (c < 32) + { // shift 1 + output[p++] = (char)0; + output[p++] = c; + } + else + { + w = s2.IndexOf(c) == -1 ? (char)0 : (char)s2.IndexOf(c); + if (w != (char)0) + { // shift 2 + output[p++] = (char)1; + output[p++] = w; + } + else + { + w = s3.IndexOf(c) == -1 ? (char)0 : (char)s3.IndexOf(c); + if (w != (char)0) + { + output[p++] = (char)2; + output[p++] = w; + } + else + //fprintf (stderr, "Could not encode 0x%02X, should not happen\n", c); + return false; + } + } + } + + if (p == 2 && targetposition + 2 == targetLength && sourceposition == sourceLength) + output[p++] = (char)0; // shift 1 pad at end + while (p >= 3) + { + int v = output[0] * 1600 + output[1] * 40 + output[2] + 1; + if (enc != newenc) + { + if (enc == 'c' || enc == 't' || enc == 'x') + t[targetposition++] = (char)254; // escape C40/text/X12 + else if (enc == 'x') + t[targetposition++] = (char)0x7C; // escape EDIFACT + if (newenc == 'c') + t[targetposition++] = (char)230; + if (newenc == 't') + t[targetposition++] = (char)239; + if (newenc == 'x') + t[targetposition++] = (char)238; + enc = newenc; + } + t[targetposition++] = (char)(v >> 8); + t[targetposition++] = (char)(v & 0xFF); + p -= (char)3; + output[0] = output[3]; + output[1] = output[4]; + output[2] = output[5]; + } + } + while (p != (char)0 && sourceposition < sourceLength); + } + break; + case 'e': // EDIFACT + { + char[] output = new char[4]; + char p = (char)0; + if (enc != newenc) + { // can only be from C40/Text/X12 + t[targetposition++] = (char)254; + enc = 'a'; + } + while (sourceposition < sourceLength && /*encoding[sourceposition].ToString(CultureInfo.InvariantCulture).ToLower() == "e"*/ + char.ToLower(encoding[sourceposition]) == 'e' && p < 4) + output[p++] = s[sourceposition++]; + if (p < 4) + { + output[p++] = (char)0x1F; + enc = 'a'; + } // termination + t[targetposition] = (char)((s[0] & 0x3F) << 2); + t[targetposition++] |= (char)((s[1] & 0x30) >> 4); + t[targetposition] = (char)((s[1] & 0x0F) << 4); + if (p == 2) + targetposition++; + else + { + t[targetposition++] |= (char)((s[2] & 0x3C) >> 2); + t[targetposition] = (char)((s[2] & 0x03) << 6); + t[targetposition++] |= (char)(s[3] & 0x3F); + } + } + break; + case 'a': // ASCII + if (enc != newenc) + { + if (enc == 'c' || enc == 't' || enc == 'x') + t[targetposition++] = (char)254; // escape C40/text/X12 + else + t[targetposition++] = (char)0x7C; // escape EDIFACT + } + enc = 'a'; + if (sourceLength - sourceposition >= 2 && char.IsDigit(s[sourceposition]) && char.IsDigit(s[sourceposition + 1])) + { + t[targetposition++] = (char)((s[sourceposition] - '0') * 10 + s[sourceposition + 1] - '0' + 130); + sourceposition += 2; + } + else if (s[sourceposition] > 127) + { + t[targetposition++] = (char)235; + t[targetposition++] = (char)(s[sourceposition++] - 127); + } + else + t[targetposition++] = (char)(s[sourceposition++] + 1); + break; + case 'b': // Binary + { + int l = 0; // how much to encode + if (encoding != null) + { + int p; + for (p = sourceposition; p < sourceLength && /*encoding[p].ToString(CultureInfo.InvariantCulture).ToLower() == "b"*/ char.ToLower(encoding[p]) == 'b'; p++) + l++; + } + t[targetposition++] = (char)231; // base256 + if (l < 250) + { + t[targetposition] = (char)State255(l, targetposition); + targetposition++; + } + else + { + t[targetposition] = (char)State255(249 + (l / 250), targetposition); + targetposition++; + t[targetposition] = (char)State255(l % 250, targetposition); + targetposition++; + } + while (l-- != 0 && targetposition < targetLength) + { + t[targetposition] = (char)State255(s[sourceposition++], targetposition); + targetposition++; + } + enc = 'a'; // reverse to ASCII at end + } + break; + // default: + // fprintf (stderr, "Unknown encoding %c\n", newenc); + // return 0; // failed + } + } + if (len != 0) + len = targetposition; + if (targetposition < targetLength && enc != 'a') + { + if (enc == 'c' || enc == 'x' || enc == 't') + t[targetposition++] = (char)254; // escape X12/C40/Text + else + t[targetposition++] = (char)0x7C; // escape EDIFACT + } + + if (targetposition < targetLength) + t[targetposition++] = (char)129; // pad + + while (targetposition < targetLength) + { // more padding + int v = 129 + (((targetposition + 1) * 149) % 253) + 1; // see Annex H + if (v > 254) + v -= 254; + t[targetposition++] = (char)v; + } + if (targetposition > targetLength || sourceposition < sourceLength) + return false; // did not fit + return true; // OK + } + + int State255(int value, int position) + { + return ((value + (((position + 1) * 149) % 255) + 1) % 256); + } + + /// + /// Places the data in the right positions according to Annex M of the ECC200 specification. + /// + void Ecc200Placement(ref int[] array, int NR, int NC) + { + int r; + int c; + int p; + + // invalidate + for (r = 0; r < NR; r++) + for (c = 0; c < NC; c++) + array[r * NC + c] = 0; + // start + p = 1; + r = 4; + c = 0; + do + { + // check corner + if (r == NR && (c == 0)) + Ecc200PlacementCornerA(ref array, NR, NC, p++); + if (r == NR - 2 && c == 0 && ((NC % 4) != 0)) + Ecc200PlacementCornerB(ref array, NR, NC, p++); + if (r == NR - 2 && c == 0 && ((NC % 8) == 4)) + Ecc200PlacementCornerC(ref array, NR, NC, p++); + if (r == NR + 4 && c == 2 && ((NC % 8) == 0)) + Ecc200PlacementCornerD(ref array, NR, NC, p++); + // up/right + do + { + if (r < NR && c >= 0 && array[r * NC + c] == 0) + Ecc200PlacementBlock(ref array, NR, NC, r, c, p++); + r -= 2; + c += 2; + } + while (r >= 0 && c < NC); + r++; + c += 3; + // down/left + do + { + if (r >= 0 && c < NC && array[r * NC + c] == 0) + Ecc200PlacementBlock(ref array, NR, NC, r, c, p++); + r += 2; + c -= 2; + } + while (r < NR && c >= 0); + r += 3; + c++; + } + while (r < NR || c < NC); + // unfilled corner + if (array[NR * NC - 1] == 0) + array[NR * NC - 1] = array[NR * NC - NC - 2] = 1; + } + + /// + /// Places the ECC200 bits in the right positions. + /// + void Ecc200PlacementBit(ref int[] array, int NR, int NC, int r, int c, int p, int b) + { + if (r < 0) + { + r += NR; + c += 4 - ((NR + 4) % 8); + } + if (c < 0) + { + c += NC; + r += 4 - ((NC + 4) % 8); + } + array[r * NC + c] = (p << 3) + b; + } + + void Ecc200PlacementBlock(ref int[] array, int NR, int NC, int r, int c, int p) + { + Ecc200PlacementBit(ref array, NR, NC, r - 2, c - 2, p, 7); + Ecc200PlacementBit(ref array, NR, NC, r - 2, c - 1, p, 6); + Ecc200PlacementBit(ref array, NR, NC, r - 1, c - 2, p, 5); + Ecc200PlacementBit(ref array, NR, NC, r - 1, c - 1, p, 4); + Ecc200PlacementBit(ref array, NR, NC, r - 1, c - 0, p, 3); + Ecc200PlacementBit(ref array, NR, NC, r - 0, c - 2, p, 2); + Ecc200PlacementBit(ref array, NR, NC, r - 0, c - 1, p, 1); + Ecc200PlacementBit(ref array, NR, NC, r - 0, c - 0, p, 0); + } + + void Ecc200PlacementCornerA(ref int[] array, int NR, int NC, int p) + { + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 0, p, 7); + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 1, p, 6); + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 2, p, 5); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 2, p, 4); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 1, p, 3); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 1, p, 2); + Ecc200PlacementBit(ref array, NR, NC, 2, NC - 1, p, 1); + Ecc200PlacementBit(ref array, NR, NC, 3, NC - 1, p, 0); + } + + void Ecc200PlacementCornerB(ref int[] array, int NR, int NC, int p) + { + Ecc200PlacementBit(ref array, NR, NC, NR - 3, 0, p, 7); + Ecc200PlacementBit(ref array, NR, NC, NR - 2, 0, p, 6); + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 0, p, 5); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 4, p, 4); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 3, p, 3); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 2, p, 2); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 1, p, 1); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 1, p, 0); + } + + void Ecc200PlacementCornerC(ref int[] array, int NR, int NC, int p) + { + Ecc200PlacementBit(ref array, NR, NC, NR - 3, 0, p, 7); + Ecc200PlacementBit(ref array, NR, NC, NR - 2, 0, p, 6); + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 0, p, 5); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 2, p, 4); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 1, p, 3); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 1, p, 2); + Ecc200PlacementBit(ref array, NR, NC, 2, NC - 1, p, 1); + Ecc200PlacementBit(ref array, NR, NC, 3, NC - 1, p, 0); + } + + void Ecc200PlacementCornerD(ref int[] array, int NR, int NC, int p) + { + Ecc200PlacementBit(ref array, NR, NC, NR - 1, 0, p, 7); + Ecc200PlacementBit(ref array, NR, NC, NR - 1, NC - 1, p, 6); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 3, p, 5); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 2, p, 4); + Ecc200PlacementBit(ref array, NR, NC, 0, NC - 1, p, 3); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 3, p, 2); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 2, p, 1); + Ecc200PlacementBit(ref array, NR, NC, 1, NC - 1, p, 0); + } + + /// + /// Calculate and append the Reed Solomon Code. + /// + void Ecc200(char[] binary, int bytes, int datablock, int rsblock) + { + int blocks = (bytes + 2) / datablock; + int b; + InitGalois(0x12d); + InitReedSolomon(rsblock, 1); + for (b = 0; b < blocks; b++) + { + int[] buf = new int[256]; + int[] ecc = new int[256]; + int n, + p = 0; + for (n = b; n < bytes; n += blocks) + buf[p++] = binary[n]; + EncodeReedSolomon(p, buf, ref ecc); + p = rsblock - 1; // comes back reversed + for (n = b; n < rsblock * blocks; n += blocks) + binary[bytes + n] = (char)ecc[p--]; + } + } + + static int gfpoly; + static int symsize; // in bits + static int logmod; // 2**symsize - 1 + static int rlen; + + static int[] log = null; + static int[] alog = null; + static int[] rspoly = null; + + /// + /// Initialize the Galois Field. + /// + /// + public static void InitGalois(int poly) + { + int m; + int b; + int p; + int v; + + // Return storage from previous setup + if (log != null) + { + log = null; + alog = null; + rspoly = null; + } + // Find the top bit, and hence the symbol size + for (b = 1, m = 0; b <= poly; b <<= 1) + m++; + b >>= 1; + m--; + gfpoly = poly; + symsize = m; + + // Calculate the log/alog tables + logmod = (1 << m) - 1; + log = new int[logmod + 1]; + alog = new int[logmod]; + + for (p = 1, v = 0; v < logmod; v++) + { + alog[v] = p; + log[p] = v; + p <<= 1; + if ((p & b) != 0) //DaSt: check! + p ^= poly; + } + } + + /// + /// Initializes the Reed-Solomon Encoder. + /// + public static void InitReedSolomon(int nsym, int index) + { + int i; + int k; + + if (rspoly != null) + rspoly = null; + rspoly = new int[nsym + 1]; + + rlen = nsym; + + rspoly[0] = 1; + for (i = 1; i <= nsym; i++) + { + rspoly[i] = 1; + for (k = i - 1; k > 0; k--) + { + if (rspoly[k] != 0) //DaSt: check! + rspoly[k] = alog[(log[rspoly[k]] + index) % logmod]; + rspoly[k] ^= rspoly[k - 1]; + } + rspoly[0] = alog[(log[rspoly[0]] + index) % logmod]; + index++; + } + } + + /// + /// Encodes the Reed-Solomon encoding + /// + public void EncodeReedSolomon(int length, int[] data, ref int[] result) + { + int i; + int k; + int m; + for (i = 0; i < rlen; i++) + result[i] = 0; + for (i = 0; i < length; i++) + { + m = result[rlen - 1] ^ data[i]; + for (k = rlen - 1; k > 0; k--) + { + if ((m != 0) && (rspoly[k] != 0)) //DaSt: check! + result[k] = result[k - 1] ^ alog[(log[m] + log[rspoly[k]]) % logmod]; + else + result[k] = result[k - 1]; + } + if ((m != 0) && (rspoly[0] != 0)) //DaSt: check! + result[0] = alog[(log[m] + log[rspoly[0]]) % logmod]; + else + result[0] = 0; + } + } + + /// + /// Creates a DataMatrix image object. + /// + /// A hex string like "AB 08 C3...". + /// I.e. 26 for a 26x26 matrix + public XImage CreateImage(char[] code, int size)//(string code, int size) + { + return CreateImage(code, size, size, 10); + } + + /// + /// Creates a DataMatrix image object. + /// + public XImage CreateImage(char[] code, int rows, int columns) + { + return CreateImage(code, rows, columns, 10); + } + + /// + /// Creates a DataMatrix image object. + /// + public XImage CreateImage(char[] code, int rows, int columns, int pixelsize) + { +#if GDI + Bitmap bm = new Bitmap(columns * pixelsize, rows * pixelsize); + using (Graphics gfx = Graphics.FromImage(bm)) + { + gfx.FillRectangle(System.Drawing.Brushes.White, new Rectangle(0, 0, columns * pixelsize, rows * pixelsize)); + + for (int i = rows - 1; i >= 0; i--) + { + for (int j = 0; j < columns; j++) + { + if (code[((rows - 1) - i) * columns + j] == (char)1) + gfx.FillRectangle(System.Drawing.Brushes.Black, j * pixelsize, i * pixelsize, pixelsize, pixelsize); + } + } + } + XImage image = XImage.FromGdiPlusImage(bm); + image.Interpolate = false; + return image; +#endif +#if WPF + // WPFHACK + return null; +#endif +#if CORE || NETFX_CORE || UWP || DNC10 + return null; +#endif + } + + struct Ecc200Block + { + public readonly int Height; + public readonly int Width; + public readonly int CellHeight; + public readonly int CellWidth; + public readonly int Bytes; + public readonly int DataBlock; + public readonly int RSBlock; + + public Ecc200Block(int h, int w, int ch, int cw, int bytes, int dataBlock, int rsBlock) + { + Height = h; + Width = w; + CellHeight = ch; + CellWidth = cw; + Bytes = bytes; + DataBlock = dataBlock; + RSBlock = rsBlock; + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.opensource.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.opensource.cs new file mode 100644 index 00000000..e105046b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/DataMatrixImage.opensource.cs @@ -0,0 +1,239 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// David Stephensen +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif + + + +// ======================================================================================== +// ======================================================================================== +// ===== THIS CLASS IS A FAKE. THE OPEN SOURCE VERSION OF PDFSHARP DOES NOT IMPLEMENT ===== +// ===== A DATAMATRIX CODE. THIS IS BECAUSE OF THE ISO COPYRIGHT. ===== +// ======================================================================================== +// ======================================================================================== + +// Even if it looks like a datamatrix code it is just random + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Creates the XImage object for a DataMatrix. + /// Important note for OpenSource version of PDFsharp: + /// The generated image object only contains random data. + /// If you need the correct implementation as defined in the ISO/IEC 16022:2000 specification, + /// please contact empira Software GmbH via www.pdfsharp.com. + /// + internal class DataMatrixImage + { + public static XImage GenerateMatrixImage(string text, string encoding, int rows, int columns) + { + DataMatrixImage dataMatrixImage = new DataMatrixImage(text, encoding, rows, columns); + return dataMatrixImage.DrawMatrix(); + } + + public DataMatrixImage(string text, string encoding, int rows, int columns) + { + this.text = text; + this.encoding = encoding; + this.rows = rows; + this.columns = columns; + } + + string text; + string encoding; + int rows; + int columns; + + /// + /// Possible ECC200 Matrixes + /// + static Ecc200Block[] ecc200Sizes = + { + new Ecc200Block( 10, 10, 10, 10, 3, 3, 5), // + new Ecc200Block( 12, 12, 12, 12, 5, 5, 7), // + new Ecc200Block( 8, 18, 8, 18, 5, 5, 7), // + new Ecc200Block( 14, 14, 14, 14, 8, 8, 10), // + new Ecc200Block( 8, 32, 8, 16, 10, 10, 11), // + new Ecc200Block( 16, 16, 16, 16, 12, 12, 12), // + new Ecc200Block( 12, 26, 12, 26, 16, 16, 14), // + new Ecc200Block( 18, 18, 18, 18, 18, 18, 14), // + new Ecc200Block( 20, 20, 20, 20, 22, 22, 18), // + new Ecc200Block( 12, 36, 12, 18, 22, 22, 18), // + new Ecc200Block( 22, 22, 22, 22, 30, 30, 20), // + new Ecc200Block( 16, 36, 16, 18, 32, 32, 24), // + new Ecc200Block( 24, 24, 24, 24, 36, 36, 24), // + new Ecc200Block( 26, 26, 26, 26, 44, 44, 28), // + new Ecc200Block( 16, 48, 16, 24, 49, 49, 28), // + new Ecc200Block( 32, 32, 16, 16, 62, 62, 36), // + new Ecc200Block( 36, 36, 18, 18, 86, 86, 42), // + new Ecc200Block( 40, 40, 20, 20, 114, 114, 48), // + new Ecc200Block( 44, 44, 22, 22, 144, 144, 56), // + new Ecc200Block( 48, 48, 24, 24, 174, 174, 68), // + new Ecc200Block( 52, 52, 26, 26, 204, 102, 42), // + new Ecc200Block( 64, 64, 16, 16, 280, 140, 56), // + new Ecc200Block( 72, 72, 18, 18, 368, 92, 36), // + new Ecc200Block( 80, 80, 20, 20, 456, 114, 48), // + new Ecc200Block( 88, 88, 22, 22, 576, 144, 56), // + new Ecc200Block( 96, 96, 24, 24, 696, 174, 68), // + new Ecc200Block(104, 104, 26, 26, 816, 136, 56), // + new Ecc200Block(120, 120, 20, 20, 1050, 175, 68), // + new Ecc200Block(132, 132, 22, 22, 1304, 163, 62), // + new Ecc200Block(144, 144, 24, 24, 1558, 156, 62), // 156*4+155*2 + new Ecc200Block( 0, 0, 0, 0, 0, 0, 0) // terminate + }; + + public XImage DrawMatrix() + { + return CreateImage(DataMatrix(), this.rows, this.columns); + } + + /// + /// Creates the DataMatrix code. + /// + internal char[] DataMatrix() + { + int matrixColumns = this.columns; + int matrixRows = this.rows; + Ecc200Block matrix = new Ecc200Block(0, 0, 0, 0, 0, 0, 0); + + foreach (Ecc200Block eccmatrix in ecc200Sizes) + { + matrix = eccmatrix; + if (matrix.Width != columns || matrix.Height != rows) + continue; + else + break; + } + + char[] grid = new char[matrixColumns * matrixRows]; + Random rand = new Random(); + + for (int ccol = 0; ccol < matrixColumns; ccol++) + grid[ccol] = (char)1; + + for (int rrows = 1; rrows < matrixRows; rrows++) + { + grid[rrows * matrixRows] = (char)1; + for (int ccol = 1; ccol < matrixColumns; ccol++) + grid[rrows * matrixRows + ccol] = (char)rand.Next(2); + } + + if (grid == null || matrixColumns == 0) + return null; //No barcode produced; + return grid; + } + + /// + /// Encodes the DataMatrix. + /// + internal char[] Iec16022Ecc200(int columns, int rows, string encoding, int barcodelen, string barcode, int len, int max, int ecc) + { + return null; + } + + /// + /// Creates a DataMatrix image object. + /// + /// A hex string like "AB 08 C3...". + /// I.e. 26 for a 26x26 matrix + public XImage CreateImage(char[] code, int size)//(string code, int size) + { + return CreateImage(code, size, size, 10); + } + + /// + /// Creates a DataMatrix image object. + /// + public XImage CreateImage(char[] code, int rows, int columns) + { + return CreateImage(code, rows, columns, 10); + } + + /// + /// Creates a DataMatrix image object. + /// + public XImage CreateImage(char[] code, int rows, int columns, int pixelsize) + { +#if GDI + Bitmap bm = new Bitmap(columns * pixelsize, rows * pixelsize); + using (Graphics gfx = Graphics.FromImage(bm)) + { + gfx.FillRectangle(System.Drawing.Brushes.White, new Rectangle(0, 0, columns * pixelsize, rows * pixelsize)); + + for (int i = rows - 1; i >= 0; i--) + { + for (int j = 0; j < columns; j++) + { + if (code[((rows - 1) - i) * columns + j] == (char)1) + gfx.FillRectangle(System.Drawing.Brushes.Black, j * pixelsize, i * pixelsize, pixelsize, pixelsize); + } + } + System.Drawing.Pen pen = new System.Drawing.Pen(System.Drawing.Color.Firebrick, pixelsize); + gfx.DrawLine(pen, 0, 0, rows * pixelsize, columns * pixelsize); + gfx.DrawLine(pen, columns * pixelsize, 0, 0, rows * pixelsize); + } + XImage image = XImage.FromGdiPlusImage(bm); + image.Interpolate = false; + return image; +#elif WPF + return null; +#endif + } + } + + struct Ecc200Block + { + public int Height; + public int Width; + public int CellHeight; + public int CellWidth; + public int Bytes; + public int DataBlock; + public int RSBlock; + + public Ecc200Block(int h, int w, int ch, int cw, int bytes, int datablock, int rsblock) + { + Height = h; + Width = w; + CellHeight = ch; + CellWidth = cw; + Bytes = bytes; + DataBlock = datablock; + RSBlock = rsblock; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/MatrixCode.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/MatrixCode.cs new file mode 100644 index 00000000..ebe3e03e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/MatrixCode.cs @@ -0,0 +1,137 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// David Stephensen +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Represents the base class of all 2D codes. + /// + public abstract class MatrixCode : CodeBase + { + /// + /// Initializes a new instance of the class. + /// + public MatrixCode(string text, string encoding, int rows, int columns, XSize size) + : base(text, size, CodeDirection.LeftToRight) + { + _encoding = encoding; + if (String.IsNullOrEmpty(_encoding)) + _encoding = new String('a', Text.Length); + + if (columns < rows) + { + _rows = columns; + _columns = rows; + } + else + { + _columns = columns; + _rows = rows; + } + + Text = text; + } + + /// + /// Gets or sets the encoding. docDaSt + /// + public string Encoding + { + get { return _encoding; } + set + { + _encoding = value; + _matrixImage = null; + } + } + string _encoding; + + /// + /// docDaSt + /// + public int Columns + { + get { return _columns; } + set + { + _columns = value; + _matrixImage = null; + } + } + int _columns; + + /// + /// docDaSt + /// + public int Rows + { + get { return _rows; } + set + { + _rows = value; + _matrixImage = null; + } + } + int _rows; + + /// + /// docDaSt + /// + public new string Text + { + get { return base.Text; } + set + { + base.Text = value; + _matrixImage = null; + } + } + + internal XImage MatrixImage + { + get { return _matrixImage; } + set { _matrixImage = value; } + } + XImage _matrixImage; + + /// + /// When implemented in a derived class renders the 2D code. + /// + protected internal abstract void Render(XGraphics gfx, XBrush brush, XPoint center); + + /// + /// Determines whether the specified string can be used as Text for this matrix code type. + /// + protected override void CheckCode(string text) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/OmrData.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/OmrData.cs new file mode 100644 index 00000000..d0a0425b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/OmrData.cs @@ -0,0 +1,130 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ +#if true_ + /// + /// Represents the data coded within the OMR code. + /// + class OmrData + { + private OmrData() + { + } + + public static OmrData ForTesting + { + get + { + OmrData data = new OmrData(); + data.AddMarkDescription("LK"); + data.AddMarkDescription("DGR"); + data.AddMarkDescription("GM1"); + data.AddMarkDescription("GM2"); + data.AddMarkDescription("GM4"); + data.AddMarkDescription("GM8"); + data.AddMarkDescription("GM16"); + data.AddMarkDescription("GM32"); + data.AddMarkDescription("ZS1"); + data.AddMarkDescription("ZS2"); + data.AddMarkDescription("ZS3"); + data.AddMarkDescription("ZS4"); + data.AddMarkDescription("ZS5"); + data.InitMarks(); + return data; + } + } + + ///// + ///// NYI: Get OMR description read from text file. + ///// + ///// An OmrData object. + //public static OmrData FromDescriptionFile(string filename) + //{ + // throw new NotImplementedException(); + //} + + /// + /// Adds a mark description by name. + /// + /// The name to for setting or unsetting the mark. + private void AddMarkDescription(string name) + { + if (_marksInitialized) + throw new InvalidOperationException(BcgSR.OmrAlreadyInitialized); + + _nameToIndex[name] = AddedDescriptions; + ++AddedDescriptions; + } + + private void InitMarks() + { + if (AddedDescriptions == 0) + throw new InvalidOperationException(); + + _marks = new bool[AddedDescriptions]; + _marks.Initialize(); + _marksInitialized = true; + } + + private int FindIndex(string name) + { + if (!_marksInitialized) + InitMarks(); + + if (!_nameToIndex.Contains(name)) + throw new ArgumentException(BcgSR.InvalidMarkName(name)); + + return (int)_nameToIndex[name]; + } + + public void SetMark(string name) + { + int idx = FindIndex(name); + _marks[idx] = true; + } + + public void UnsetMark(string name) + { + int idx = FindIndex(name); + _marks[idx] = false; + } + + public bool[] Marks + { + get { return _marks; } + } + System.Collections.Hash_table nameToIndex = new Hash_table(); + bool[] marks; + int addedDescriptions = 0; + bool marksInitialized = false; + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/ThickThinBarcodeRenderer.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/ThickThinBarcodeRenderer.cs new file mode 100644 index 00000000..9791afe3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/ThickThinBarcodeRenderer.cs @@ -0,0 +1,186 @@ +// +// PDFsharp - A library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System; + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Internal base class for several bar code types. + /// + public abstract class ThickThinBarCode : BarCode // TODO: The name is not optimal + { + /// + /// Initializes a new instance of the class. + /// + public ThickThinBarCode(string code, XSize size, CodeDirection direction) + : base(code, size, direction) + { } + + internal override void InitRendering(BarCodeRenderInfo info) + { + base.InitRendering(info); + CalcThinBarWidth(info); + info.BarHeight = Size.Height; + // HACK in ThickThinBarCode + if (TextLocation != TextLocation.None) + info.BarHeight *= 4.0 / 5; + +#if DEBUG_ + XColor back = XColors.LightSalmon; + back.A = 0.3; + XSolidBrush brush = new XSolidBrush(back); + info.Gfx.DrawRectangle(brush, new XRect(info.Center - size / 2, size)); +#endif + switch (Direction) + { + case CodeDirection.RightToLeft: + info.Gfx.RotateAtTransform(180, info.Position); + break; + + case CodeDirection.TopToBottom: + info.Gfx.RotateAtTransform(90, info.Position); + break; + + case CodeDirection.BottomToTop: + info.Gfx.RotateAtTransform(-90, info.Position); + break; + } + } + + /// + /// Gets or sets the ration between thick an thin lines. Must be between 2 and 3. + /// Optimal and also default value is 2.6. + /// + public override double WideNarrowRatio + { + get { return _wideNarrowRatio; } + set + { + if (value > 3 || value < 2) + throw new ArgumentOutOfRangeException("value", BcgSR.Invalid2of5Relation); + _wideNarrowRatio = value; + } + } + double _wideNarrowRatio = 2.6; + + /// + /// Renders a thick or thin line for the bar code. + /// + /// + /// Determines whether a thick or a thin line is about to be rendered. + internal void RenderBar(BarCodeRenderInfo info, bool isThick) + { + double barWidth = GetBarWidth(info, isThick); + double height = Size.Height; + double xPos = info.CurrPos.X; + double yPos = info.CurrPos.Y; + + switch (TextLocation) + { + case TextLocation.AboveEmbedded: + height -= info.Gfx.MeasureString(Text, info.Font).Height; + yPos += info.Gfx.MeasureString(Text, info.Font).Height; + break; + case TextLocation.BelowEmbedded: + height -= info.Gfx.MeasureString(Text, info.Font).Height; + break; + } + + XRect rect = new XRect(xPos, yPos, barWidth, height); + info.Gfx.DrawRectangle(info.Brush, rect); + info.CurrPos.X += barWidth; + } + + /// + /// Renders a thick or thin gap for the bar code. + /// + /// + /// Determines whether a thick or a thin gap is about to be rendered. + internal void RenderGap(BarCodeRenderInfo info, bool isThick) + { + info.CurrPos.X += GetBarWidth(info, isThick); + } + + /// + /// Renders a thick bar before or behind the code. + /// + internal void RenderTurboBit(BarCodeRenderInfo info, bool startBit) + { + if (startBit) + info.CurrPos.X -= 0.5 + GetBarWidth(info, true); + else + info.CurrPos.X += 0.5; //GetBarWidth(info, true); + + RenderBar(info, true); + + if (startBit) + info.CurrPos.X += 0.5; //GetBarWidth(info, true); + } + + internal void RenderText(BarCodeRenderInfo info) + { + if (info.Font == null) + info.Font = new XFont("Courier New", Size.Height / 6); + XPoint center = info.Position + CalcDistance(Anchor, AnchorType.TopLeft, Size); + + switch (TextLocation) + { + case TextLocation.Above: + center = new XPoint(center.X, center.Y - info.Gfx.MeasureString(Text, info.Font).Height); + info.Gfx.DrawString(Text, info.Font, info.Brush, new XRect(center, Size), XStringFormats.TopCenter); + break; + case TextLocation.AboveEmbedded: + info.Gfx.DrawString(Text, info.Font, info.Brush, new XRect(center, Size), XStringFormats.TopCenter); + break; + case TextLocation.Below: + center = new XPoint(center.X, info.Gfx.MeasureString(Text, info.Font).Height + center.Y); + info.Gfx.DrawString(Text, info.Font, info.Brush, new XRect(center, Size), XStringFormats.BottomCenter); + break; + case TextLocation.BelowEmbedded: + info.Gfx.DrawString(Text, info.Font, info.Brush, new XRect(center, Size), XStringFormats.BottomCenter); + break; + } + } + + /// + /// Gets the width of a thick or a thin line (or gap). CalcLineWidth must have been called before. + /// + /// + /// Determines whether a thick line's with shall be returned. + internal double GetBarWidth(BarCodeRenderInfo info, bool isThick) + { + if (isThick) + return info.ThinBarWidth * _wideNarrowRatio; + return info.ThinBarWidth; + } + + internal abstract void CalcThinBarWidth(BarCodeRenderInfo info); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/AnchorType.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/AnchorType.cs new file mode 100644 index 00000000..869fadc8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/AnchorType.cs @@ -0,0 +1,82 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Specifies whether and how the text is displayed at the code area. + /// + public enum AnchorType + { + /// + /// The anchor is located top left. + /// + TopLeft, + + /// + /// The anchor is located top center. + /// + TopCenter, + + /// + /// The anchor is located top right. + /// + TopRight, + + /// + /// The anchor is located middle left. + /// + MiddleLeft, + + /// + /// The anchor is located middle center. + /// + MiddleCenter, + + /// + /// The anchor is located middle right. + /// + MiddleRight, + + /// + /// The anchor is located bottom left. + /// + BottomLeft, + + /// + /// The anchor is located bottom center. + /// + BottomCenter, + + /// + /// The anchor is located bottom right. + /// + BottomRight, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeDirection.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeDirection.cs new file mode 100644 index 00000000..0df06b41 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeDirection.cs @@ -0,0 +1,57 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Specifies the drawing direction of the code. + /// + public enum CodeDirection + { + /// + /// Does not rotate the code. + /// + LeftToRight, + + /// + /// Rotates the code 180 at the anchor position. + /// + BottomToTop, + + /// + /// Rotates the code 180 at the anchor position. + /// + RightToLeft, + + /// + /// Rotates the code 180 at the anchor position. + /// + TopToBottom, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeType.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeType.cs new file mode 100644 index 00000000..4ddfe9ca --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/CodeType.cs @@ -0,0 +1,59 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Specifies the type of the bar code. + /// + public enum CodeType + { + /// + /// The standard 2 of 5 interleaved bar code. + /// + // ReSharper disable once InconsistentNaming + Code2of5Interleaved, + + /// + /// The standard 3 of 9 bar code. + /// + // ReSharper disable once InconsistentNaming + Code3of9Standard, + + /// + /// The OMR code. + /// + Omr, + + /// + /// The data matrix code. + /// + DataMatrix, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/DataMatrixEncoding.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/DataMatrixEncoding.cs new file mode 100644 index 00000000..bf63a580 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/DataMatrixEncoding.cs @@ -0,0 +1,68 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// docDaSt + /// + public enum DataMatrixEncoding + { + /// + /// docDaSt + /// + Ascii, + + /// + /// docDaSt + /// + C40, + + /// + /// docDaSt + /// + Text, + + /// + /// docDaSt + /// + X12, + + /// + /// docDaSt + /// + // ReSharper disable once InconsistentNaming + EDIFACT, + + /// + /// docDaSt + /// + Base256 + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/MarkDistance.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/MarkDistance.cs new file mode 100644 index 00000000..2095f07d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/MarkDistance.cs @@ -0,0 +1,50 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + ///// + ///// Valid mark distances for OMR Codes. + ///// + //public enum MarkDistance + //{ + // /// + // /// 2/6 inch, valid for printing with 6 lpi. (line height = 12 pt). + // /// + // Inch1_6, + // /// + // /// 2/6 inch, valid for printing with 6 lpi (line height = 12 pt). + // /// + // Inch2_6, + // /// + // /// 2/8 inch, valid for printing with 8 lpi (line height = 9 pt). + // /// + // Inch2_8 + //} +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/TextLocation.cs b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/TextLocation.cs new file mode 100644 index 00000000..bd0674ed --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.BarCodes/enums/TextLocation.cs @@ -0,0 +1,64 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Klaus Potzesny +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.BarCodes +{ + /// + /// Specifies whether and how the text is displayed at the code. + /// + public enum TextLocation + { + /// + /// No text is drawn. + /// + None, + + /// + /// The text is located above the code. + /// + Above, + + /// + /// The text is located below the code. + /// + Below, + + + /// + /// The text is located above within the code. + /// + AboveEmbedded, + + + /// + /// The text is located below within the code. + /// + BelowEmbedded, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Internal/IImageImporter.cs b/src/PDFsharp/src/PdfSharp/Drawing.Internal/IImageImporter.cs new file mode 100644 index 00000000..ab5ad2ca --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Internal/IImageImporter.cs @@ -0,0 +1,328 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Pdf; + +namespace PdfSharp.Drawing +{ + /// + /// This interface will be implemented by specialized classes, one for JPEG, one for BMP, one for PNG, one for GIF. Maybe more. + /// + internal interface IImageImporter + { + /// + /// Imports the image. Returns null if the image importer does not support the format. + /// + ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document); + + /// + /// Prepares the image data needed for the PDF file. + /// + ImageData PrepareImage(ImagePrivateData data); + } + + // $THHO Add IDispose?. + /// + /// Helper for dealing with Stream data. + /// + internal class StreamReaderHelper + { + internal StreamReaderHelper(Stream stream) + { +#if GDI || WPF + _stream = stream; + MemoryStream ms = stream as MemoryStream; + if (ms == null) + { + // THHO4STLA byte[] or MemoryStream? + _ownedMemoryStream = ms = new MemoryStream(); + CopyStream(stream, ms); + // For .NET 4: stream.CopyTo(ms); + } + _data = ms.GetBuffer(); + _length = (int)ms.Length; +#else + // For WinRT there is no GetBuffer() => alternative implementation for WinRT. + // TODO: Are there advantages of GetBuffer()? It should reduce LOH fragmentation. + _stream = stream; + _stream.Position = 0; + if (_stream.Length > int.MaxValue) + throw new ArgumentException("Stream is too large.", "stream"); + _length = (int)_stream.Length; + _data = new byte[_length]; + _stream.Read(_data, 0, _length); +#endif + } + + internal byte GetByte(int offset) + { + if (_currentOffset + offset >= _length) + { + Debug.Assert(false); + return 0; + } + return _data[_currentOffset + offset]; + } + + internal ushort GetWord(int offset, bool bigEndian) + { + return (ushort)(bigEndian ? + GetByte(offset) * 256 + GetByte(offset + 1) : + GetByte(offset) + GetByte(offset + 1) * 256); + } + + internal uint GetDWord(int offset, bool bigEndian) + { + return (uint)(bigEndian ? + GetWord(offset, true) * 65536 + GetWord(offset + 2, true) : + GetWord(offset, false) + GetWord(offset + 2, false) * 65536); + } + + private static void CopyStream(Stream input, Stream output) + { + byte[] buffer = new byte[65536]; + int read; + while ((read = input.Read(buffer, 0, buffer.Length)) > 0) + { + output.Write(buffer, 0, read); + } + } + + /// + /// Resets this instance. + /// + public void Reset() + { + _currentOffset = 0; + } + + /// + /// Gets the original stream. + /// + public Stream OriginalStream + { + get { return _stream; } + } + private readonly Stream _stream; + + internal int CurrentOffset + { + get { return _currentOffset; } + set { _currentOffset = value; } + } + private int _currentOffset; + + /// + /// Gets the data as byte[]. + /// + public byte[] Data + { + get { return _data; } + } + private readonly byte[] _data; + + /// + /// Gets the length of Data. + /// + public int Length + { + get { return _length; } + } + + private readonly int _length; + +#if GDI || WPF + /// + /// Gets the owned memory stream. Can be null if no MemoryStream was created. + /// + public MemoryStream OwnedMemoryStream + { + get { return _ownedMemoryStream; } + } + private readonly MemoryStream _ownedMemoryStream; +#endif + } + + /// + /// The imported image. + /// + internal abstract class ImportedImage + { + /// + /// Initializes a new instance of the class. + /// + protected ImportedImage(IImageImporter importer, ImagePrivateData data, PdfDocument document) + { + Data = data; + _document = document; + data.Image = this; + _importer = importer; + } + + + /// + /// Gets information about the image. + /// + public ImageInformation Information + { + get { return _information; } + private set { _information = value; } + } + private ImageInformation _information = new ImageInformation(); + + /// + /// Gets a value indicating whether image data for the PDF file was already prepared. + /// + public bool HasImageData + { + get { return _imageData != null; } + } + + /// + /// Gets the image data needed for the PDF file. + /// + public ImageData ImageData + { + get { if(!HasImageData) _imageData = PrepareImageData(); return _imageData; } + private set { _imageData = value; } + } + private ImageData _imageData; + + internal virtual ImageData PrepareImageData() + { + throw new NotImplementedException(); + } + + private IImageImporter _importer; + internal ImagePrivateData Data; + internal readonly PdfDocument _document; + } + + /// + /// Public information about the image, filled immediately. + /// Note: The stream will be read and decoded on the first call to PrepareImageData(). + /// ImageInformation can be filled for corrupted images that will throw an expection on PrepareImageData(). + /// + internal class ImageInformation + { + internal enum ImageFormats + { + /// + /// Standard JPEG format (RGB). + /// + JPEG, + /// + /// Grayscale JPEG format. + /// + JPEGGRAY, + /// + /// JPEG file with inverted CMYK, thus RGBW. + /// + JPEGRGBW, + /// + /// JPEG file with CMYK. + /// + JPEGCMYK, + Palette1, + Palette4, + Palette8, + RGB24, + ARGB32 + } + + internal ImageFormats ImageFormat; + + internal uint Width; + internal uint Height; + + /// + /// The horizontal DPI (dots per inch). Can be 0 if not supported by the image format. + /// Note: JFIF (JPEG) files may contain either DPI or DPM or just the aspect ratio. Windows BMP files will contain DPM. Other formats may support any combination, including none at all. + /// + internal decimal HorizontalDPI; + /// + /// The vertical DPI (dots per inch). Can be 0 if not supported by the image format. + /// + internal decimal VerticalDPI; + + /// + /// The horizontal DPM (dots per meter). Can be 0 if not supported by the image format. + /// + internal decimal HorizontalDPM; + /// + /// The vertical DPM (dots per meter). Can be 0 if not supported by the image format. + /// + internal decimal VerticalDPM; + + /// + /// The horizontal component of the aspect ratio. Can be 0 if not supported by the image format. + /// Note: Aspect ratio will be set if either DPI or DPM was set, but may also be available in the absence of both DPI and DPM. + /// + internal decimal HorizontalAspectRatio; + /// + /// The vertical component of the aspect ratio. Can be 0 if not supported by the image format. + /// + internal decimal VerticalAspectRatio; + + /// + /// The colors used. Only valid for images with palettes, will be 0 otherwise. + /// + internal uint ColorsUsed; + } + + /// + /// Contains internal data. This includes a reference to the Stream if data for PDF was not yet prepared. + /// + internal abstract class ImagePrivateData + { + internal ImagePrivateData() + { + } + + /// + /// Gets the image. + /// + public ImportedImage Image + { + get { return _image; } + internal set { _image = value; } + } + private ImportedImage _image; + } + + /// + /// Contains data needed for PDF. Will be prepared when needed. + /// + internal abstract class ImageData + { + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporter.cs b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporter.cs new file mode 100644 index 00000000..3f91724b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporter.cs @@ -0,0 +1,92 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; +using System.IO; +using PdfSharp.Pdf; + +namespace PdfSharp.Drawing.Internal +{ + /// + /// The class that imports images of various formats. + /// + internal class ImageImporter + { + // TODO Make a singleton! + /// + /// Gets the image importer. + /// + public static ImageImporter GetImageImporter() + { + return new ImageImporter(); + } + + private ImageImporter() + { + _importers.Add(new ImageImporterJpeg()); + _importers.Add(new ImageImporterBmp()); + // TODO: Special importer for PDF? Or dealt with at a higher level? + } + + /// + /// Imports the image. + /// + public ImportedImage ImportImage(Stream stream, PdfDocument document) + { + StreamReaderHelper helper = new StreamReaderHelper(stream); + + // Try all registered importers to see if any of them can handle the image. + foreach (IImageImporter importer in _importers) + { + helper.Reset(); + ImportedImage image = importer.ImportImage(helper, document); + if (image != null) + return image; + } + return null; + } + +#if GDI || WPF || CORE + /// + /// Imports the image. + /// + public ImportedImage ImportImage(string filename, PdfDocument document) + { + ImportedImage ii; + using (Stream fs = File.OpenRead(filename)) + { + ii = ImportImage(fs, document); + } + return ii; + } +#endif + + private readonly List _importers = new List(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterBmp.cs b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterBmp.cs new file mode 100644 index 00000000..7b01bc61 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterBmp.cs @@ -0,0 +1,681 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf; +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Drawing.Internal +{ + // $THHO THHO4THHO add support for PdfDocument.Options. + internal class ImageImporterBmp : ImageImporterRoot, IImageImporter + { + public ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document) + { + try + { + stream.CurrentOffset = 0; + int offsetImageData; + if (TestBitmapFileHeader(stream, out offsetImageData)) + { + // Magic: TestBitmapFileHeader updates stream.CurrentOffset on success. + + ImagePrivateDataBitmap ipd = new ImagePrivateDataBitmap(stream.Data, stream.Length); + ImportedImage ii = new ImportedImageBitmap(this, ipd, document); + if (TestBitmapInfoHeader(stream, ii, offsetImageData)) + { + //stream.CurrentOffset = offsetImageData; + return ii; + } + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch (Exception) + { + } + return null; + } + + private bool TestBitmapFileHeader(StreamReaderHelper stream, out int offset) + { + offset = 0; + // File must start with "BM". + if (stream.GetWord(0, true) == 0x424d) + { + int filesize = (int)stream.GetDWord(2, false); + // Integrity check: filesize set in BM header should match size of the stream. + // We test "<" instead of "!=" to allow extra bytes at the end of the stream. + if (filesize < stream.Length) + return false; + + offset = (int)stream.GetDWord(10, false); + stream.CurrentOffset += 14; + return true; + } + return false; + } + + private bool TestBitmapInfoHeader(StreamReaderHelper stream, ImportedImage ii, int offset) + { + int size = (int)stream.GetDWord(0, false); + if (size == 40 || size == 108 || size == 124) // sizeof BITMAPINFOHEADER == 40, sizeof BITMAPV4HEADER == 108, sizeof BITMAPV5HEADER == 124 + { + uint width = stream.GetDWord(4, false); + int height = (int)stream.GetDWord(8, false); + int planes = stream.GetWord(12, false); + int bitcount = stream.GetWord(14, false); + int compression = (int)stream.GetDWord(16, false); + int sizeImage = (int)stream.GetDWord(20, false); + int xPelsPerMeter = (int)stream.GetDWord(24, false); + int yPelsPerMeter = (int)stream.GetDWord(28, false); + uint colorsUsed = stream.GetDWord(32, false); + uint colorsImportant = stream.GetDWord(36, false); + // TODO Integrity and plausibility checks. + if (sizeImage != 0 && sizeImage + offset > stream.Length) + return false; + + ImagePrivateDataBitmap privateData = (ImagePrivateDataBitmap)ii.Data; + + // Return true only for supported formats. + if (compression == 0 || compression == 3) // BI_RGB == 0, BI_BITFIELDS == 3 + { + ((ImagePrivateDataBitmap)ii.Data).Offset = offset; + ((ImagePrivateDataBitmap)ii.Data).ColorPaletteOffset = stream.CurrentOffset + size; + ii.Information.Width = width; + ii.Information.Height = (uint)Math.Abs(height); + ii.Information.HorizontalDPM = xPelsPerMeter; + ii.Information.VerticalDPM = yPelsPerMeter; + privateData.FlippedImage = height < 0; + if (planes == 1 && bitcount == 24) + { + // RGB24 + ii.Information.ImageFormat = ImageInformation.ImageFormats.RGB24; + + // TODO: Verify Mask if size >= 108 && compression == 3. + return true; + } + if (planes == 1 && bitcount == 32) + { + // ARGB32 + //ii.Information.ImageFormat = ImageInformation.ImageFormats.ARGB32; + ii.Information.ImageFormat = compression == 0 ? + ImageInformation.ImageFormats.RGB24 : + ImageInformation.ImageFormats.ARGB32; + + // TODO: tell RGB from ARGB. Idea: assume RGB if alpha is always 0. + + // TODO: Verify Mask if size >= 108 && compression == 3. + return true; + } + if (planes == 1 && bitcount == 8) + { + // Palette8 + ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette8; + ii.Information.ColorsUsed = colorsUsed; + + return true; + } + if (planes == 1 && bitcount == 4) + { + // Palette8 + ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette4; + ii.Information.ColorsUsed = colorsUsed; + + return true; + } + if (planes == 1 && bitcount == 1) + { + // Palette8 + ii.Information.ImageFormat = ImageInformation.ImageFormats.Palette1; + ii.Information.ColorsUsed = colorsUsed; + + return true; + } + // TODO Implement more formats! + } + } + return false; + } + + + public ImageData PrepareImage(ImagePrivateData data) + { + throw new NotImplementedException(); + } + } + + /// + /// Bitmap refers to the format used in PDF. Will be used for BMP, PNG, TIFF, GIF and others. + /// + internal class ImportedImageBitmap : ImportedImage + { + /// + /// Initializes a new instance of the class. + /// + public ImportedImageBitmap(IImageImporter importer, ImagePrivateDataBitmap data, PdfDocument document) + : base(importer, data, document) + { } + + internal override ImageData PrepareImageData() + { + ImagePrivateDataBitmap data = (ImagePrivateDataBitmap)Data; + ImageDataBitmap imageData = new ImageDataBitmap(_document); + //imageData.Data = data.Data; + //imageData.Length = data.Length; + + data.CopyBitmap(imageData); + + return imageData; + } + } + + // THHO4THHO Maybe there will be derived classes for direct bitmaps vs. palettized bitmaps or so. Time will tell. + + /// + /// Contains data needed for PDF. Will be prepared when needed. + /// Bitmap refers to the format used in PDF. Will be used for BMP, PNG, TIFF, GIF and others. + /// + internal class ImageDataBitmap : ImageData + { + private ImageDataBitmap() + { + } + + internal ImageDataBitmap(PdfDocument document) + { + _document = document; + } + + /// + /// Gets the data. + /// + public byte[] Data + { + get { return _data; } + internal set { _data = value; } + } + private byte[] _data; + + /// + /// Gets the length. + /// + public int Length + { + get { return _length; } + internal set { _length = value; } + } + private int _length; + + /// + /// Gets the data. + /// + public byte[] DataFax + { + get { return _dataFax; } + internal set { _dataFax = value; } + } + private byte[] _dataFax; + + /// + /// Gets the length. + /// + public int LengthFax + { + get { return _lengthFax; } + internal set { _lengthFax = value; } + } + private int _lengthFax; + + public byte[] AlphaMask + { + get { return _alphaMask; } + internal set { _alphaMask = value; } + } + private byte[] _alphaMask; + + public int AlphaMaskLength + { + get { return _alphaMaskLength; } + internal set { _alphaMaskLength = value; } + } + private int _alphaMaskLength; + + public byte[] BitmapMask + { + get { return _bitmapMask; } + internal set { _bitmapMask = value; } + } + private byte[] _bitmapMask; + + public int BitmapMaskLength + { + get { return _bitmapMaskLength; } + internal set { _bitmapMaskLength = value; } + } + private int _bitmapMaskLength; + + public byte[] PaletteData + { + get { return _paletteData; } + set { _paletteData = value; } + } + private byte[] _paletteData; + + public int PaletteDataLength + { + get { return _paletteDataLength; } + set { _paletteDataLength = value; } + } + private int _paletteDataLength; + + public bool SegmentedColorMask; + + public int IsBitonal; + + public int K; + + public bool IsGray; + + internal readonly PdfDocument _document; + } + + /// + /// Image data needed for PDF bitmap images. + /// + internal class ImagePrivateDataBitmap : ImagePrivateData + { + /// + /// Initializes a new instance of the class. + /// + public ImagePrivateDataBitmap(byte[] data, int length) + { + _data = data; + _length = length; + } + + /// + /// Gets the data. + /// + public byte[] Data + { + get { return _data; } + //internal set { _data = value; } + } + private readonly byte[] _data; + + /// + /// Gets the length. + /// + public int Length + { + get { return _length; } + //internal set { _length = value; } + } + private readonly int _length; + + /// + /// True if first line is the top line, false if first line is the bottom line of the image. When needed, lines will be reversed while converting data into PDF format. + /// + internal bool FlippedImage; + + /// + /// The offset of the image data in Data. + /// + internal int Offset; + + /// + /// The offset of the color palette in Data. + /// + internal int ColorPaletteOffset; + + internal void CopyBitmap(ImageDataBitmap dest) + { + switch (Image.Information.ImageFormat) + { + case ImageInformation.ImageFormats.ARGB32: + CopyTrueColorMemoryBitmap(3, 8, true, dest); + break; + + case ImageInformation.ImageFormats.RGB24: + CopyTrueColorMemoryBitmap(4, 8, false, dest); + break; + + case ImageInformation.ImageFormats.Palette8: + CopyIndexedMemoryBitmap(8, dest); + break; + + case ImageInformation.ImageFormats.Palette4: + CopyIndexedMemoryBitmap(4, dest); + break; + + case ImageInformation.ImageFormats.Palette1: + CopyIndexedMemoryBitmap(1, dest); + break; + + + + default: + throw new NotImplementedException(); + } + } + + /// + /// Copies images without color palette. + /// + /// 4 (32bpp RGB), 3 (24bpp RGB, 32bpp ARGB) + /// 8 + /// true (ARGB), false (RGB) + /// Destination + private void CopyTrueColorMemoryBitmap(int components, int bits, bool hasAlpha, ImageDataBitmap dest) + { + int width = (int)Image.Information.Width; + int height = (int)Image.Information.Height; + + int logicalComponents = components; + if (components == 4) + logicalComponents = 3; + + byte[] imageData = new byte[components * width * height]; + + bool hasMask = false; + bool hasAlphaMask = false; + byte[] alphaMask = hasAlpha ? new byte[width * height] : null; + MonochromeMask mask = hasAlpha ? + new MonochromeMask(width, height) : null; + + int nFileOffset = Offset; + int nOffsetRead = 0; + if (logicalComponents == 3) + { + for (int y = 0; y < height; ++y) + { + // TODO Handle Flipped. + int nOffsetWrite = 3 * (height - 1 - y) * width; + int nOffsetWriteAlpha = 0; + if (hasAlpha) + { + mask.StartLine(y); + nOffsetWriteAlpha = (height - 1 - y) * width; + } + + for (int x = 0; x < width; ++x) + { + imageData[nOffsetWrite] = Data[nFileOffset + nOffsetRead + 2]; + imageData[nOffsetWrite + 1] = Data[nFileOffset + nOffsetRead + 1]; + imageData[nOffsetWrite + 2] = Data[nFileOffset + nOffsetRead]; + if (hasAlpha) + { + mask.AddPel(Data[nFileOffset + nOffsetRead + 3]); + alphaMask[nOffsetWriteAlpha] = Data[nFileOffset + nOffsetRead + 3]; + if (!hasMask || !hasAlphaMask) + { + if (Data[nFileOffset + nOffsetRead + 3] != 255) + { + hasMask = true; + if (Data[nFileOffset + nOffsetRead + 3] != 0) + hasAlphaMask = true; + } + } + ++nOffsetWriteAlpha; + } + nOffsetRead += hasAlpha ? 4 : components; + nOffsetWrite += 3; + } + nOffsetRead = 4 * ((nOffsetRead + 3) / 4); // Align to 32 bit boundary + } + } + else if (components == 1) + { + // Grayscale + throw new NotImplementedException("Image format not supported (grayscales)."); + } + + dest.Data = imageData; + dest.Length = imageData.Length; + + if (alphaMask != null) + { + dest.AlphaMask = alphaMask; + dest.AlphaMaskLength = alphaMask.Length; + } + + if (mask != null) + { + dest.BitmapMask = mask.MaskData; + dest.BitmapMaskLength = mask.MaskData.Length; + } + } + + private void CopyIndexedMemoryBitmap(int bits/*, ref bool hasAlpha*/, ImageDataBitmap dest) + { + int firstMaskColor = -1, lastMaskColor = -1; + bool segmentedColorMask = false; + + int bytesColorPaletteOffset = ((ImagePrivateDataBitmap)Image.Data).ColorPaletteOffset; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER + + int bytesFileOffset = ((ImagePrivateDataBitmap)Image.Data).Offset; + uint paletteColors = Image.Information.ColorsUsed; + int width = (int)Image.Information.Width; + int height = (int)Image.Information.Height; + + MonochromeMask mask = new MonochromeMask(width, height); + + bool isGray = bits == 8 && (paletteColors == 256 || paletteColors == 0); + int isBitonal = 0; // 0: false; >0: true; <0: true (inverted) + byte[] paletteData = new byte[3 * paletteColors]; + for (int color = 0; color < paletteColors; ++color) + { + paletteData[3 * color] = Data[bytesColorPaletteOffset + 4 * color + 2]; + paletteData[3 * color + 1] = Data[bytesColorPaletteOffset + 4 * color + 1]; + paletteData[3 * color + 2] = Data[bytesColorPaletteOffset + 4 * color + 0]; + if (isGray) + isGray = paletteData[3 * color] == paletteData[3 * color + 1] && + paletteData[3 * color] == paletteData[3 * color + 2]; + + if (Data[bytesColorPaletteOffset + 4 * color + 3] < 128) + { + // We treat this as transparency: + if (firstMaskColor == -1) + firstMaskColor = color; + if (lastMaskColor == -1 || lastMaskColor == color - 1) + lastMaskColor = color; + if (lastMaskColor != color) + segmentedColorMask = true; + } + //else + //{ + // // We treat this as opacity: + //} + } + + if (bits == 1) + { + if (paletteColors == 0) + isBitonal = 1; + if (paletteColors == 2) + { + if (paletteData[0] == 0 && + paletteData[1] == 0 && + paletteData[2] == 0 && + paletteData[3] == 255 && + paletteData[4] == 255 && + paletteData[5] == 255) + isBitonal = 1; // Black on white + if (paletteData[5] == 0 && + paletteData[4] == 0 && + paletteData[3] == 0 && + paletteData[2] == 255 && + paletteData[1] == 255 && + paletteData[0] == 255) + isBitonal = -1; // White on black + } + } + + // NYI: (no sample found where this was required) + // if (segmentedColorMask = true) + // { ... } + + bool isFaxEncoding = false; + byte[] imageData = new byte[((width * bits + 7) / 8) * height]; + byte[] imageDataFax = null; + int k = 0; + + + if (bits == 1 && dest._document.Options.EnableCcittCompressionForBilevelImages) + { + // TODO: flag/option? + // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array. + //byte[] temp = new byte[imageData.Length]; + //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height); + + // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here. + //byte[] temp2D = new byte[imageData.Length]; + //uint dpiY = (uint)image.VerticalResolution; + //uint kTmp = 0; + //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp); + //k = (int) kTmp; + + byte[] tempG4 = new byte[imageData.Length]; + int ccittSizeG4 = PdfImage.DoFaxEncodingGroup4(ref tempG4, Data, (uint)bytesFileOffset, (uint)width, (uint)height); + + isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0; + if (isFaxEncoding) + { + //if (ccittSize == 0) + // ccittSize = 0x7fffffff; + if (ccittSizeG4 == 0) + ccittSizeG4 = 0x7fffffff; + //if (ccittSize <= ccittSizeG4) + //{ + // Array.Resize(ref temp, ccittSize); + // imageDataFax = temp; + // k = 0; + //} + //else + { + Array.Resize(ref tempG4, ccittSizeG4); + imageDataFax = tempG4; + k = -1; + } + } + } + + //if (!isFaxEncoding) + { + int bytesOffsetRead = 0; + if (bits == 8 || bits == 4 || bits == 1) + { + int bytesPerLine = (width * bits + 7) / 8; + for (int y = 0; y < height; ++y) + { + mask.StartLine(y); + int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8); + for (int x = 0; x < bytesPerLine; ++x) + { + if (isGray) + { + // Lookup the gray value from the palette: + imageData[bytesOffsetWrite] = paletteData[3 * Data[bytesFileOffset + bytesOffsetRead]]; + } + else + { + // Store the palette index. + imageData[bytesOffsetWrite] = Data[bytesFileOffset + bytesOffsetRead]; + } + if (firstMaskColor != -1) + { + int n = Data[bytesFileOffset + bytesOffsetRead]; + if (bits == 8) + { + // TODO???: segmentedColorMask == true => bad mask NYI + mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor)); + } + else if (bits == 4) + { + // TODO???: segmentedColorMask == true => bad mask NYI + int n1 = (n & 0xf0) / 16; + int n2 = (n & 0x0f); + mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor)); + mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor)); + } + else if (bits == 1) + { + // TODO???: segmentedColorMask == true => bad mask NYI + for (int bit = 1; bit <= 8; ++bit) + { + int n1 = (n & 0x80) / 128; + mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor)); + n *= 2; + } + } + } + bytesOffsetRead += 1; + bytesOffsetWrite += 1; + } + bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary + } + } + else + { + throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3"); + } + } + + dest.Data = imageData; + dest.Length = imageData.Length; + + if (imageDataFax != null) + { + dest.DataFax = imageDataFax; + dest.LengthFax = imageDataFax.Length; + } + + dest.IsGray = isGray; + dest.K = k; + dest.IsBitonal = isBitonal; + + dest.PaletteData = paletteData; + dest.PaletteDataLength = paletteData.Length; + dest.SegmentedColorMask = segmentedColorMask; + + //if (alphaMask != null) + //{ + // dest.AlphaMask = alphaMask; + // dest.AlphaMaskLength = alphaMask.Length; + //} + + if (mask != null && firstMaskColor != -1) + { + dest.BitmapMask = mask.MaskData; + dest.BitmapMaskLength = mask.MaskData.Length; + } + + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterJpeg.cs b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterJpeg.cs new file mode 100644 index 00000000..2ffbfdd5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterJpeg.cs @@ -0,0 +1,363 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf; + +namespace PdfSharp.Drawing.Internal +{ + // ReSharper disable once InconsistentNaming + internal class ImageImporterJpeg : ImageImporterRoot, IImageImporter + { + // TODO Find information about JPEG2000. + + // Notes: JFIF is big-endian. + + public ImportedImage ImportImage(StreamReaderHelper stream, PdfDocument document) + { + try + { + + stream.CurrentOffset = 0; + // Test 2 magic bytes. + if (TestFileHeader(stream)) + { + // Skip over 2 magic bytes. + stream.CurrentOffset += 2; + + ImagePrivateDataDct ipd = new ImagePrivateDataDct(stream.Data, stream.Length); + ImportedImage ii = new ImportedImageJpeg(this, ipd, document); + if (TestJfifHeader(stream, ii)) + { + bool colorHeader = false, infoHeader = false; + + while (MoveToNextHeader(stream)) + { + if (TestColorFormatHeader(stream, ii)) + { + colorHeader = true; + } + else if (TestInfoHeader(stream, ii)) + { + infoHeader = true; + } + } + if (colorHeader && infoHeader) + return ii; + } + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch (Exception) + { + } + return null; + } + + private bool TestFileHeader(StreamReaderHelper stream) + { + // File must start with 0xffd8. + return stream.GetWord(0, true) == 0xffd8; + } + + private bool TestJfifHeader(StreamReaderHelper stream, ImportedImage ii) + { + // The App0 header should be the first header in every JFIF file. + if (stream.GetWord(0, true) == 0xffe0) + { + // Now check for text "JFIF". + if (stream.GetDWord(4, true) == 0x4a464946) + { + int blockLength = stream.GetWord(2, true); + if (blockLength >= 16) + { + int version = stream.GetWord(9, true); + int units = stream.GetByte(11); + int densityX = stream.GetWord(12, true); + int densityY = stream.GetWord(14, true); + + switch (units) + { + case 0: // Aspect ratio only. + ii.Information.HorizontalAspectRatio = densityX; + ii.Information.VerticalAspectRatio = densityY; + break; + case 1: // DPI. + ii.Information.HorizontalDPI = densityX; + ii.Information.VerticalDPI = densityY; + break; + case 2: // DPCM. + ii.Information.HorizontalDPM = densityX * 100; + ii.Information.VerticalDPM = densityY * 100; + break; + } + + // More information here? More tests? + return true; + } + } + } + return false; + } + + private bool TestColorFormatHeader(StreamReaderHelper stream, ImportedImage ii) + { + // The SOS header (start of scan). + if (stream.GetWord(0, true) == 0xffda) + { + int components = stream.GetByte(4); + if (components < 1 || components > 4 || components == 2) + return false; + // 1 for grayscale, 3 for RGB, 4 for CMYK. + + int blockLength = stream.GetWord(2, true); + // Integrity check: correct size? + if (blockLength != 6 + 2 * components) + return false; + + // Eventually do more tests here. + // Magic: we assume that all JPEG files with 4 components are RGBW (inverted CMYK) and not CMYK. + // We add a test to tell CMYK from RGBW when we encounter a test file in CMYK format. + ii.Information.ImageFormat = components == 3 ? ImageInformation.ImageFormats.JPEG : + (components == 1 ? ImageInformation.ImageFormats.JPEGGRAY : ImageInformation.ImageFormats.JPEGRGBW); + + return true; + } + return false; + } + + private bool TestInfoHeader(StreamReaderHelper stream, ImportedImage ii) + { + // The SOF header (start of frame). + int header = stream.GetWord(0, true); + if (header >= 0xffc0 && header <= 0xffc3 || + header >= 0xffc9 && header <= 0xffcb) + { + // Lines in image. + int sizeY = stream.GetWord(5, true); + // Samples per line. + int sizeX = stream.GetWord(7, true); + + // $THHO TODO: Check if we always get useful information here. + ii.Information.Width = (uint)sizeX; + ii.Information.Height = (uint)sizeY; + + return true; + } + return false; + } + + private bool MoveToNextHeader(StreamReaderHelper stream) + { + int blockLength = stream.GetWord(2, true); + + int headerMagic = stream.GetByte(0); + int headerType = stream.GetByte(1); + + if (headerMagic == 0xff) + { + // EOI: last header. + if (headerType == 0xd9) + return false; + + // Check for standalone markers. + if (headerType == 0x01 || headerType >= 0xd0 && headerType <= 0xd7) + { + stream.CurrentOffset += 2; + return true; + } + + // Now assume header with block size. + stream.CurrentOffset += 2 + blockLength; + return true; + } + return false; + } + + public ImageData PrepareImage(ImagePrivateData data) + { + throw new NotImplementedException(); + } + + //int GetJpgSizeTestCode(byte[] pData, uint FileSizeLow, out int pWidth, out int pHeight) + //{ + // pWidth = -1; + // pHeight = -1; + + // int i = 0; + + + // if ((pData[i] == 0xFF) && (pData[i + 1] == 0xD8) && (pData[i + 2] == 0xFF) && (pData[i + 3] == 0xE0)) + // { + // i += 4; + + // // Check for valid JPEG header (null terminated JFIF) + // if ((pData[i + 2] == 'J') && (pData[i + 3] == 'F') && (pData[i + 4] == 'I') && (pData[i + 5] == 'F') + // && (pData[i + 6] == 0x00)) + // { + + // //Retrieve the block length of the first block since the first block will not contain the size of file + // int block_length = pData[i] * 256 + pData[i + 1]; + + // while (i < FileSizeLow) + // { + // //Increase the file index to get to the next block + // i += block_length; + + // if (i >= FileSizeLow) + // { + // //Check to protect against segmentation faults + // return -1; + // } + + // if (pData[i] != 0xFF) + // { + // return -2; + // } + + // if (pData[i + 1] == 0xC0) + // { + // //0xFFC0 is the "Start of frame" marker which contains the file size + // //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y] + // pHeight = pData[i + 5] * 256 + pData[i + 6]; + // pWidth = pData[i + 7] * 256 + pData[i + 8]; + + // return 0; + // } + // else + // { + // i += 2; //Skip the block marker + + // //Go to the next block + // block_length = pData[i] * 256 + pData[i + 1]; + // } + // } + + // //If this point is reached then no size was found + // return -3; + // } + // else + // { + // return -4; + // } //Not a valid JFIF string + // } + // else + // { + // return -5; + // } //Not a valid SOI header + + // //return -6; + //} // GetJpgSize + } + + /// + /// Imported JPEG image. + /// + internal class ImportedImageJpeg : ImportedImage + { + /// + /// Initializes a new instance of the class. + /// + public ImportedImageJpeg(IImageImporter importer, ImagePrivateDataDct data, PdfDocument document) + : base(importer, data, document) + { } + + internal override ImageData PrepareImageData() + { + ImagePrivateDataDct data = (ImagePrivateDataDct)Data; + ImageDataDct imageData = new ImageDataDct(); + imageData.Data = data.Data; + imageData.Length = data.Length; + + return imageData; + } + } + + /// + /// Contains data needed for PDF. Will be prepared when needed. + /// + internal class ImageDataDct : ImageData + { + /// + /// Gets the data. + /// + public byte[] Data + { + get { return _data; } + internal set { _data = value; } + } + private byte[] _data; + + /// + /// Gets the length. + /// + public int Length + { + get { return _length; } + internal set { _length = value; } + } + private int _length; + } + + /*internal*/ + /// + /// Private data for JPEG images. + /// + internal class ImagePrivateDataDct : ImagePrivateData + { + /// + /// Initializes a new instance of the class. + /// + public ImagePrivateDataDct(byte[] data, int length) + { + _data = data; + _length = length; + } + + /// + /// Gets the data. + /// + public byte[] Data + { + get { return _data; } + //internal set { _data = value; } + } + private readonly byte[] _data; + + /// + /// Gets the length. + /// + public int Length + { + get { return _length; } + //internal set { _length = value; } + } + private readonly int _length; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterRoot.cs b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterRoot.cs new file mode 100644 index 00000000..ea110a00 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Internal/ImageImporterRoot.cs @@ -0,0 +1,35 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.Internal +{ + internal abstract class ImageImporterRoot + { + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Layout/XTextFormatter.cs b/src/PDFsharp/src/PdfSharp/Drawing.Layout/XTextFormatter.cs new file mode 100644 index 00000000..f0445c67 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Layout/XTextFormatter.cs @@ -0,0 +1,399 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Drawing.Layout +{ + /// + /// Represents a very simple text formatter. + /// If this class does not satisfy your needs on formatting paragraphs I recommend to take a look + /// at MigraDoc Foundation. Alternatively you should copy this class in your own source code and modify it. + /// + public class XTextFormatter + { + /// + /// Initializes a new instance of the class. + /// + public XTextFormatter(XGraphics gfx) + { + if (gfx == null) + throw new ArgumentNullException("gfx"); + _gfx = gfx; + } + readonly XGraphics _gfx; + + /// + /// Gets or sets the text. + /// + /// The text. + public string Text + { + get { return _text; } + set { _text = value; } + } + string _text; + + /// + /// Gets or sets the font. + /// + public XFont Font + { + get { return _font; } + set + { + if (value == null) + throw new ArgumentNullException("Font"); + _font = value; + + _lineSpace = _font.GetHeight(); // old: _font.GetHeight(_gfx); + _cyAscent = _lineSpace * _font.CellAscent / _font.CellSpace; + _cyDescent = _lineSpace * _font.CellDescent / _font.CellSpace; + + // HACK in XTextFormatter + _spaceWidth = _gfx.MeasureString("xx", value).Width; + _spaceWidth -= _gfx.MeasureString("xx", value).Width; + } + } + XFont _font; + double _lineSpace; + double _cyAscent; + double _cyDescent; + double _spaceWidth; + + /// + /// Gets or sets the bounding box of the layout. + /// + public XRect LayoutRectangle + { + get { return _layoutRectangle; } + set { _layoutRectangle = value; } + } + XRect _layoutRectangle; + + /// + /// Gets or sets the alignment of the text. + /// + public XParagraphAlignment Alignment + { + get { return _alignment; } + set { _alignment = value; } + } + XParagraphAlignment _alignment = XParagraphAlignment.Left; + + /// + /// Draws the text. + /// + /// The text to be drawn. + /// The font. + /// The text brush. + /// The layout rectangle. + public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle) + { + DrawString(text, font, brush, layoutRectangle, XStringFormats.TopLeft); + } + + /// + /// Draws the text. + /// + /// The text to be drawn. + /// The font. + /// The text brush. + /// The layout rectangle. + /// The format. Must be XStringFormat.TopLeft + public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format) + { + if (text == null) + throw new ArgumentNullException("text"); + if (font == null) + throw new ArgumentNullException("font"); + if (brush == null) + throw new ArgumentNullException("brush"); + if (format.Alignment != XStringAlignment.Near || format.LineAlignment != XLineAlignment.Near) + throw new ArgumentException("Only TopLeft alignment is currently implemented."); + + Text = text; + Font = font; + LayoutRectangle = layoutRectangle; + + if (text.Length == 0) + return; + + CreateBlocks(); + + CreateLayout(); + + double dx = layoutRectangle.Location.X; + double dy = layoutRectangle.Location.Y + _cyAscent; + int count = _blocks.Count; + for (int idx = 0; idx < count; idx++) + { + Block block = _blocks[idx]; + if (block.Stop) + break; + if (block.Type == BlockType.LineBreak) + continue; + _gfx.DrawString(block.Text, font, brush, dx + block.Location.X, dy + block.Location.Y); + } + } + + void CreateBlocks() + { + _blocks.Clear(); + int length = _text.Length; + bool inNonWhiteSpace = false; + int startIndex = 0, blockLength = 0; + for (int idx = 0; idx < length; idx++) + { + char ch = _text[idx]; + + // Treat CR and CRLF as LF + if (ch == Chars.CR) + { + if (idx < length - 1 && _text[idx + 1] == Chars.LF) + idx++; + ch = Chars.LF; + } + if (ch == Chars.LF) + { + if (blockLength != 0) + { + string token = _text.Substring(startIndex, blockLength); + _blocks.Add(new Block(token, BlockType.Text, + _gfx.MeasureString(token, _font).Width)); + } + startIndex = idx + 1; + blockLength = 0; + _blocks.Add(new Block(BlockType.LineBreak)); + } + // The non-breaking space is whitespace, so we treat it like non-whitespace. + else if (ch != Chars.NonBreakableSpace && char.IsWhiteSpace(ch)) + { + if (inNonWhiteSpace) + { + string token = _text.Substring(startIndex, blockLength); + _blocks.Add(new Block(token, BlockType.Text, + _gfx.MeasureString(token, _font).Width)); + startIndex = idx + 1; + blockLength = 0; + } + else + { + blockLength++; + } + } + else + { + inNonWhiteSpace = true; + blockLength++; + } + } + if (blockLength != 0) + { + string token = _text.Substring(startIndex, blockLength); + _blocks.Add(new Block(token, BlockType.Text, + _gfx.MeasureString(token, _font).Width)); + } + } + + void CreateLayout() + { + double rectWidth = _layoutRectangle.Width; + double rectHeight = _layoutRectangle.Height - _cyAscent - _cyDescent; + int firstIndex = 0; + double x = 0, y = 0; + int count = _blocks.Count; + for (int idx = 0; idx < count; idx++) + { + Block block = _blocks[idx]; + if (block.Type == BlockType.LineBreak) + { + if (Alignment == XParagraphAlignment.Justify) + _blocks[firstIndex].Alignment = XParagraphAlignment.Left; + AlignLine(firstIndex, idx - 1, rectWidth); + firstIndex = idx + 1; + x = 0; + y += _lineSpace; + if (y > rectHeight) + { + block.Stop = true; + break; + } + } + else + { + double width = block.Width; + if ((x + width <= rectWidth || x == 0) && block.Type != BlockType.LineBreak) + { + block.Location = new XPoint(x, y); + x += width + _spaceWidth; + } + else + { + AlignLine(firstIndex, idx - 1, rectWidth); + firstIndex = idx; + y += _lineSpace; + if (y > rectHeight) + { + block.Stop = true; + break; + } + block.Location = new XPoint(0, y); + x = width + _spaceWidth; + } + } + } + if (firstIndex < count && Alignment != XParagraphAlignment.Justify) + AlignLine(firstIndex, count - 1, rectWidth); + } + + /// + /// Align center, right, or justify. + /// + void AlignLine(int firstIndex, int lastIndex, double layoutWidth) + { + XParagraphAlignment blockAlignment = _blocks[firstIndex].Alignment; + if (_alignment == XParagraphAlignment.Left || blockAlignment == XParagraphAlignment.Left) + return; + + int count = lastIndex - firstIndex + 1; + if (count == 0) + return; + + double totalWidth = -_spaceWidth; + for (int idx = firstIndex; idx <= lastIndex; idx++) + totalWidth += _blocks[idx].Width + _spaceWidth; + + double dx = Math.Max(layoutWidth - totalWidth, 0); + //Debug.Assert(dx >= 0); + if (_alignment != XParagraphAlignment.Justify) + { + if (_alignment == XParagraphAlignment.Center) + dx /= 2; + for (int idx = firstIndex; idx <= lastIndex; idx++) + { + Block block = _blocks[idx]; + block.Location += new XSize(dx, 0); + } + } + else if (count > 1) // case: justify + { + dx /= count - 1; + for (int idx = firstIndex + 1, i = 1; idx <= lastIndex; idx++, i++) + { + Block block = _blocks[idx]; + block.Location += new XSize(dx * i, 0); + } + } + } + + readonly List _blocks = new List(); + + enum BlockType + { + Text, Space, Hyphen, LineBreak, + } + + /// + /// Represents a single word. + /// + class Block + { + /// + /// Initializes a new instance of the class. + /// + /// The text of the block. + /// The type of the block. + /// The width of the text. + public Block(string text, BlockType type, double width) + { + Text = text; + Type = type; + Width = width; + } + + /// + /// Initializes a new instance of the class. + /// + /// The type. + public Block(BlockType type) + { + Type = type; + } + + /// + /// The text represented by this block. + /// + public readonly string Text; + + /// + /// The type of the block. + /// + public readonly BlockType Type; + + /// + /// The width of the text. + /// + public readonly double Width; + + /// + /// The location relative to the upper left corner of the layout rectangle. + /// + public XPoint Location; + + /// + /// The alignment of this line. + /// + public XParagraphAlignment Alignment; + + /// + /// A flag indicating that this is the last block that fits in the layout rectangle. + /// + public bool Stop; + } + // TODO: + // - more XStringFormat variations + // - calculate bounding box + // - left and right indent + // - first line indent + // - margins and paddings + // - background color + // - text background color + // - border style + // - hyphens, soft hyphens, hyphenation + // - kerning + // - change font, size, text color etc. + // - line spacing + // - underline and strike-out variation + // - super- and sub-script + // - ... + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Layout/enums/XParagraphAlignment.cs b/src/PDFsharp/src/PdfSharp/Drawing.Layout/enums/XParagraphAlignment.cs new file mode 100644 index 00000000..a50529e4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Layout/enums/XParagraphAlignment.cs @@ -0,0 +1,62 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.Layout +{ + /// + /// Specifies the alignment of a paragraph. + /// + public enum XParagraphAlignment + { + /// + /// Default alignment, typically left alignment. + /// + Default, + + /// + /// The paragraph is rendered left aligned. + /// + Left, + + /// + /// The paragraph is rendered centered. + /// + Center, + + /// + /// The paragraph is rendered right aligned. + /// + Right, + + /// + /// The paragraph is rendered justified. + /// + Justify, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Pdf/PdfGraphicsState.cs b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/PdfGraphicsState.cs new file mode 100644 index 00000000..0a72925c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/PdfGraphicsState.cs @@ -0,0 +1,543 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +#endif +using PdfSharp.Internal; +using PdfSharp.Pdf; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Internal; + +// ReSharper disable CompareOfFloatsByEqualityOperator + +namespace PdfSharp.Drawing.Pdf +{ + /// + /// Represents the current PDF graphics state. + /// + /// + /// Completely revised for PDFsharp 1.4. + /// + internal sealed class PdfGraphicsState : ICloneable + { + public PdfGraphicsState(XGraphicsPdfRenderer renderer) + { + _renderer = renderer; + } + readonly XGraphicsPdfRenderer _renderer; + + public PdfGraphicsState Clone() + { + PdfGraphicsState state = (PdfGraphicsState)MemberwiseClone(); + return state; + } + + object ICloneable.Clone() + { + return Clone(); + } + + internal int Level; + + internal InternalGraphicsState InternalState; + + public void PushState() + { + // BeginGraphic + _renderer.Append("q/n"); + } + + public void PopState() + { + //BeginGraphic + _renderer.Append("Q/n"); + } + + #region Stroke + + double _realizedLineWith = -1; + int _realizedLineCap = -1; + int _realizedLineJoin = -1; + double _realizedMiterLimit = -1; + XDashStyle _realizedDashStyle = (XDashStyle)(-1); + string _realizedDashPattern; + XColor _realizedStrokeColor = XColor.Empty; + bool _realizedStrokeOverPrint; + + public void RealizePen(XPen pen, PdfColorMode colorMode) + { + const string frmt2 = Config.SignificantFigures2; + const string format = Config.SignificantFigures3; + XColor color = pen.Color; + bool overPrint = pen.Overprint; + color = ColorSpaceHelper.EnsureColorMode(colorMode, color); + + if (_realizedLineWith != pen._width) + { + _renderer.AppendFormatArgs("{0:" + format + "} w\n", pen._width); + _realizedLineWith = pen._width; + } + + if (_realizedLineCap != (int)pen._lineCap) + { + _renderer.AppendFormatArgs("{0} J\n", (int)pen._lineCap); + _realizedLineCap = (int)pen._lineCap; + } + + if (_realizedLineJoin != (int)pen._lineJoin) + { + _renderer.AppendFormatArgs("{0} j\n", (int)pen._lineJoin); + _realizedLineJoin = (int)pen._lineJoin; + } + + if (_realizedLineCap == (int)XLineJoin.Miter) + { + if (_realizedMiterLimit != (int)pen._miterLimit && (int)pen._miterLimit != 0) + { + _renderer.AppendFormatInt("{0} M\n", (int)pen._miterLimit); + _realizedMiterLimit = (int)pen._miterLimit; + } + } + + if (_realizedDashStyle != pen._dashStyle || pen._dashStyle == XDashStyle.Custom) + { + double dot = pen.Width; + double dash = 3 * dot; + + // Line width 0 is not recommended but valid. + XDashStyle dashStyle = pen.DashStyle; + if (dot == 0) + dashStyle = XDashStyle.Solid; + + switch (dashStyle) + { + case XDashStyle.Solid: + _renderer.Append("[]0 d\n"); + break; + + case XDashStyle.Dash: + _renderer.AppendFormatArgs("[{0:" + frmt2 + "} {1:" + frmt2 + "}]0 d\n", dash, dot); + break; + + case XDashStyle.Dot: + _renderer.AppendFormatArgs("[{0:" + frmt2 + "}]0 d\n", dot); + break; + + case XDashStyle.DashDot: + _renderer.AppendFormatArgs("[{0:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "}]0 d\n", dash, dot); + break; + + case XDashStyle.DashDotDot: + _renderer.AppendFormatArgs("[{0:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "} {1:" + frmt2 + "}]0 d\n", dash, dot); + break; + + case XDashStyle.Custom: + { + StringBuilder pdf = new StringBuilder("[", 256); + int len = pen._dashPattern == null ? 0 : pen._dashPattern.Length; + for (int idx = 0; idx < len; idx++) + { + if (idx > 0) + pdf.Append(' '); + pdf.Append(PdfEncoders.ToString(pen._dashPattern[idx] * pen._width)); + } + // Make an even number of values look like in GDI+ + if (len > 0 && len % 2 == 1) + { + pdf.Append(' '); + pdf.Append(PdfEncoders.ToString(0.2 * pen._width)); + } + pdf.AppendFormat(CultureInfo.InvariantCulture, "]{0:" + format + "} d\n", pen._dashOffset * pen._width); + string pattern = pdf.ToString(); + + // BUG: drice2@ageone.de reported a realizing problem + // HACK: I remove the if clause + //if (_realizedDashPattern != pattern) + { + _realizedDashPattern = pattern; + _renderer.Append(pattern); + } + } + break; + } + _realizedDashStyle = dashStyle; + } + + if (colorMode != PdfColorMode.Cmyk) + { + if (_realizedStrokeColor.Rgb != color.Rgb) + { + _renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Rgb)); + _renderer.Append(" RG\n"); + } + } + else + { + if (!ColorSpaceHelper.IsEqualCmyk(_realizedStrokeColor, color)) + { + _renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Cmyk)); + _renderer.Append(" K\n"); + } + } + + if (_renderer.Owner.Version >= 14 && (_realizedStrokeColor.A != color.A || _realizedStrokeOverPrint != overPrint)) + { + PdfExtGState extGState = _renderer.Owner.ExtGStateTable.GetExtGStateStroke(color.A, overPrint); + string gs = _renderer.Resources.AddExtGState(extGState); + _renderer.AppendFormatString("{0} gs\n", gs); + + // Must create transparency group. + if (_renderer._page != null && color.A < 1) + _renderer._page.TransparencyUsed = true; + } + _realizedStrokeColor = color; + _realizedStrokeOverPrint = overPrint; + } + + #endregion + + #region Fill + + XColor _realizedFillColor = XColor.Empty; + bool _realizedNonStrokeOverPrint; + + public void RealizeBrush(XBrush brush, PdfColorMode colorMode, int renderingMode, double fontEmSize) + { + // Rendering mode 2 is used for bold simulation. + // Reference: TABLE 5.3Text rendering modes / Page 402 + + XSolidBrush solidBrush = brush as XSolidBrush; + if (solidBrush != null) + { + XColor color = solidBrush.Color; + bool overPrint = solidBrush.Overprint; + + if (renderingMode == 0) + { + RealizeFillColor(color, overPrint, colorMode); + } + else if (renderingMode == 2) + { + // Come here in case of bold simulation. + RealizeFillColor(color, false, colorMode); + //color = XColors.Green; + RealizePen(new XPen(color, fontEmSize * Const.BoldEmphasis), colorMode); + } + else + throw new InvalidOperationException("Only rendering modes 0 and 2 are currently supported."); + } + else + { + if (renderingMode != 0) + throw new InvalidOperationException("Rendering modes other than 0 can only be used with solid color brushes."); + + XLinearGradientBrush gradientBrush = brush as XLinearGradientBrush; + if (gradientBrush != null) + { + Debug.Assert(UnrealizedCtm.IsIdentity, "Must realize ctm first."); + XMatrix matrix = _renderer.DefaultViewMatrix; + matrix.Prepend(EffectiveCtm); + PdfShadingPattern pattern = new PdfShadingPattern(_renderer.Owner); + pattern.SetupFromBrush(gradientBrush, matrix, _renderer); + string name = _renderer.Resources.AddPattern(pattern); + _renderer.AppendFormatString("/Pattern cs\n", name); + _renderer.AppendFormatString("{0} scn\n", name); + + // Invalidate fill color. + _realizedFillColor = XColor.Empty; + } + } + } + + private void RealizeFillColor(XColor color, bool overPrint, PdfColorMode colorMode) + { + color = ColorSpaceHelper.EnsureColorMode(colorMode, color); + + if (colorMode != PdfColorMode.Cmyk) + { + if (_realizedFillColor.IsEmpty || _realizedFillColor.Rgb != color.Rgb) + { + _renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Rgb)); + _renderer.Append(" rg\n"); + } + } + else + { + Debug.Assert(colorMode == PdfColorMode.Cmyk); + + if (_realizedFillColor.IsEmpty || !ColorSpaceHelper.IsEqualCmyk(_realizedFillColor, color)) + { + _renderer.Append(PdfEncoders.ToString(color, PdfColorMode.Cmyk)); + _renderer.Append(" k\n"); + } + } + + if (_renderer.Owner.Version >= 14 && (_realizedFillColor.A != color.A || _realizedNonStrokeOverPrint != overPrint)) + { + + PdfExtGState extGState = _renderer.Owner.ExtGStateTable.GetExtGStateNonStroke(color.A, overPrint); + string gs = _renderer.Resources.AddExtGState(extGState); + _renderer.AppendFormatString("{0} gs\n", gs); + + // Must create transparency group. + if (_renderer._page != null && color.A < 1) + _renderer._page.TransparencyUsed = true; + } + _realizedFillColor = color; + _realizedNonStrokeOverPrint = overPrint; + } + + internal void RealizeNonStrokeTransparency(double transparency, PdfColorMode colorMode) + { + XColor color = _realizedFillColor; + color.A = transparency; + RealizeFillColor(color, _realizedNonStrokeOverPrint, colorMode); + } + + #endregion + + #region Text + + internal PdfFont _realizedFont; + string _realizedFontName = String.Empty; + double _realizedFontSize; + int _realizedRenderingMode; // Reference: TABLE 5.2 Text state operators / Page 398 + double _realizedCharSpace; // Reference: TABLE 5.2 Text state operators / Page 398 + + public void RealizeFont(XFont font, XBrush brush, int renderingMode) + { + const string format = Config.SignificantFigures3; + + // So far rendering mode 0 (fill text) and 2 (fill, then stroke text) only. + RealizeBrush(brush, _renderer._colorMode, renderingMode, font.Size); // _renderer.page.document.Options.ColorMode); + + // Realize rendering mode. + if (_realizedRenderingMode != renderingMode) + { + _renderer.AppendFormatInt("{0} Tr\n", renderingMode); + _realizedRenderingMode = renderingMode; + } + + // Realize character spacing. + if (_realizedRenderingMode == 0) + { + if (_realizedCharSpace != 0) + { + _renderer.Append("0 Tc\n"); + _realizedCharSpace = 0; + } + } + else // _realizedRenderingMode is 2. + { + double charSpace = font.Size * Const.BoldEmphasis; + if (_realizedCharSpace != charSpace) + { + _renderer.AppendFormatDouble("{0:" + format + "} Tc\n", charSpace); + _realizedCharSpace = charSpace; + } + } + + _realizedFont = null; + string fontName = _renderer.GetFontName(font, out _realizedFont); + if (fontName != _realizedFontName || _realizedFontSize != font.Size) + { + if (_renderer.Gfx.PageDirection == XPageDirection.Downwards) + _renderer.AppendFormatFont("{0} {1:" + format + "} Tf\n", fontName, font.Size); + else + _renderer.AppendFormatFont("{0} {1:" + format + "} Tf\n", fontName, font.Size); + _realizedFontName = fontName; + _realizedFontSize = font.Size; + } + } + + public XPoint RealizedTextPosition; + + /// + /// Indicates that the text transformation matrix currently skews 20 to the right. + /// + public bool ItalicSimulationOn; + + #endregion + + #region Transformation + + /// + /// The already realized part of the current transformation matrix. + /// + public XMatrix RealizedCtm; + + /// + /// The not yet realized part of the current transformation matrix. + /// + public XMatrix UnrealizedCtm; + + /// + /// Product of RealizedCtm and UnrealizedCtm. + /// + public XMatrix EffectiveCtm; + + /// + /// Inverse of EffectiveCtm used for transformation. + /// + public XMatrix InverseEffectiveCtm; + + public XMatrix WorldTransform; + + ///// + ///// The world transform in PDF world space. + ///// + //public XMatrix EffectiveCtm + //{ + // get + // { + // //if (MustRealizeCtm) + // if (!UnrealizedCtm.IsIdentity) + // { + // XMatrix matrix = RealizedCtm; + // matrix.Prepend(UnrealizedCtm); + // return matrix; + // } + // return RealizedCtm; + // } + // //set + // //{ + // // XMatrix matrix = realizedCtm; + // // matrix.Invert(); + // // matrix.Prepend(value); + // // unrealizedCtm = matrix; + // // MustRealizeCtm = !unrealizedCtm.IsIdentity; + // //} + //} + + public void AddTransform(XMatrix value, XMatrixOrder matrixOrder) + { + // TODO: User matrixOrder +#if DEBUG + if (matrixOrder == XMatrixOrder.Append) + throw new NotImplementedException("XMatrixOrder.Append"); +#endif + XMatrix transform = value; + if (_renderer.Gfx.PageDirection == XPageDirection.Downwards) + { + // Take chirality into account and + // invert the direction of rotation. + transform.M12 = -value.M12; + transform.M21 = -value.M21; + } + UnrealizedCtm.Prepend(transform); + + WorldTransform.Prepend(value); + } + + /// + /// Realizes the CTM. + /// + public void RealizeCtm() + { + //if (MustRealizeCtm) + if (!UnrealizedCtm.IsIdentity) + { + Debug.Assert(!UnrealizedCtm.IsIdentity, "mrCtm is unnecessarily set."); + + const string format = Config.SignificantFigures7; + + double[] matrix = UnrealizedCtm.GetElements(); + // Use up to six decimal digits to prevent round up problems. + _renderer.AppendFormatArgs("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm\n", + matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); + + RealizedCtm.Prepend(UnrealizedCtm); + UnrealizedCtm = new XMatrix(); + EffectiveCtm = RealizedCtm; + InverseEffectiveCtm = EffectiveCtm; + InverseEffectiveCtm.Invert(); + } + } + #endregion + + #region Clip Path + + public void SetAndRealizeClipRect(XRect clipRect) + { + XGraphicsPath clipPath = new XGraphicsPath(); + clipPath.AddRectangle(clipRect); + RealizeClipPath(clipPath); + } + + public void SetAndRealizeClipPath(XGraphicsPath clipPath) + { + RealizeClipPath(clipPath); + } + + void RealizeClipPath(XGraphicsPath clipPath) + { +#if CORE + DiagnosticsHelper.HandleNotImplemented("RealizeClipPath"); +#endif +#if GDI + // Do not render an empty path. + if (clipPath._gdipPath.PointCount < 0) + return; +#endif +#if WPF + // Do not render an empty path. + if (clipPath._pathGeometry.Bounds.IsEmpty) + return; +#endif + _renderer.BeginGraphicMode(); + RealizeCtm(); +#if CORE + _renderer.AppendPath(clipPath._corePath); +#endif +#if GDI && !WPF + _renderer.AppendPath(clipPath._gdipPath); +#endif +#if WPF && !GDI + _renderer.AppendPath(clipPath._pathGeometry); +#endif +#if WPF && GDI + if (_renderer.Gfx.TargetContext == XGraphicTargetContext.GDI) + _renderer.AppendPath(clipPath._gdipPath); + else + _renderer.AppendPath(clipPath._pathGeometry); +#endif + _renderer.Append(clipPath.FillMode == XFillMode.Winding ? "W n\n" : "W* n\n"); + } + + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Pdf/XGraphicsPdfRenderer.cs b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/XGraphicsPdfRenderer.cs new file mode 100644 index 00000000..8194dfc0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/XGraphicsPdfRenderer.cs @@ -0,0 +1,2155 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define ITALIC_SIMULATION + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Collections.Generic; +using System.Text; +using PdfSharp.Events; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +#endif +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; +using PdfSharp.Pdf; +using PdfSharp.Pdf.Internal; +using PdfSharp.Pdf.Advanced; + +// ReSharper disable RedundantNameQualifier +// ReSharper disable CompareOfFloatsByEqualityOperator + +namespace PdfSharp.Drawing.Pdf +{ + /// + /// Represents a drawing surface for PdfPages. + /// + internal class XGraphicsPdfRenderer : IXGraphicsRenderer + { + public XGraphicsPdfRenderer(PdfPage page, XGraphics gfx, XGraphicsPdfPageOptions options) + { + _page = page; + _colorMode = page._document.Options.ColorMode; + _options = options; + _gfx = gfx; + _content = new StringBuilder(); + page.RenderContent._pdfRenderer = this; + _gfxState = new PdfGraphicsState(this); + } + + public XGraphicsPdfRenderer(XForm form, XGraphics gfx) + { + _form = form; + _colorMode = form.Owner.Options.ColorMode; + _gfx = gfx; + _content = new StringBuilder(); + form.PdfRenderer = this; + _gfxState = new PdfGraphicsState(this); + } + + /// + /// Gets the content created by this renderer. + /// + string GetContent() + { + EndPage(); + return _content.ToString(); + } + + public XGraphicsPdfPageOptions PageOptions + { + get { return _options; } + } + + public void Close() + { + if (_page != null) + { + PdfContent content2 = _page.RenderContent; + content2.CreateStream(PdfEncoders.RawEncoding.GetBytes(GetContent())); + + _gfx = null; + _page.RenderContent._pdfRenderer = null; + _page.RenderContent = null; + _page = null; + } + else if (_form != null) + { + _form._pdfForm.CreateStream(PdfEncoders.RawEncoding.GetBytes(GetContent())); + _gfx = null; + _form.PdfRenderer = null; + _form = null; + } + } + + // -------------------------------------------------------------------------------------------- + + #region Drawing + + //void SetPageLayout(down, point(0, 0), unit + + // ----- DrawLine ----------------------------------------------------------------------------- + + /// + /// Strokes a single connection of two points. + /// + public void DrawLine(XPen pen, double x1, double y1, double x2, double y2) + { + DrawLines(pen, new XPoint[] { new XPoint(x1, y1), new XPoint(x2, y2) }); + } + + // ----- DrawLines ---------------------------------------------------------------------------- + + /// + /// Strokes a series of connected points. + /// + public void DrawLines(XPen pen, XPoint[] points) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count == 0) + return; + + Realize(pen); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx++) + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + _content.Append("S\n"); + } + + // ----- DrawBezier --------------------------------------------------------------------------- + + public void DrawBezier(XPen pen, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { + DrawBeziers(pen, new XPoint[] { new XPoint(x1, y1), new XPoint(x2, y2), new XPoint(x3, y3), new XPoint(x4, y4) }); + } + + // ----- DrawBeziers -------------------------------------------------------------------------- + + public void DrawBeziers(XPen pen, XPoint[] points) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count == 0) + return; + + if ((count - 1) % 3 != 0) + throw new ArgumentException("Invalid number of points for bezier curves. Number must fulfil 4+3n.", "points"); + + Realize(pen); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx += 3) + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + points[idx].X, points[idx].Y, + points[idx + 1].X, points[idx + 1].Y, + points[idx + 2].X, points[idx + 2].Y); + + AppendStrokeFill(pen, null, XFillMode.Alternate, false); + } + + // ----- DrawCurve ---------------------------------------------------------------------------- + + public void DrawCurve(XPen pen, XPoint[] points, double tension) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count == 0) + return; + if (count < 2) + throw new ArgumentException("Not enough points", "points"); + + // See http://pubpages.unh.edu/~cs770/a5/cardinal.html // Link is down... + tension /= 3; + + Realize(pen); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + if (count == 2) + { + // Just draws a line. + AppendCurveSegment(points[0], points[0], points[1], points[1], tension); + } + else + { + AppendCurveSegment(points[0], points[0], points[1], points[2], tension); + for (int idx = 1; idx < count - 2; idx++) + AppendCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension); + AppendCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension); + } + AppendStrokeFill(pen, null, XFillMode.Alternate, false); + } + + // ----- DrawArc ------------------------------------------------------------------------------ + + public void DrawArc(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null) + throw new ArgumentNullException("pen"); + + Realize(pen); + + AppendPartialArc(x, y, width, height, startAngle, sweepAngle, PathStart.MoveTo1st, new XMatrix()); + AppendStrokeFill(pen, null, XFillMode.Alternate, false); + } + + // ----- DrawRectangle ------------------------------------------------------------------------ + + public void DrawRectangle(XPen pen, XBrush brush, double x, double y, double width, double height) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush"); + + const string format = Config.SignificantFigures3; + + Realize(pen, brush); + //AppendFormat123("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} re\n", x, y, width, -height); + AppendFormatRect("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} re\n", x, y + height, width, height); + + if (pen != null && brush != null) + _content.Append("B\n"); + else if (pen != null) + _content.Append("S\n"); + else + _content.Append("f\n"); + } + + // ----- DrawRectangles ----------------------------------------------------------------------- + + public void DrawRectangles(XPen pen, XBrush brush, XRect[] rects) + { + int count = rects.Length; + for (int idx = 0; idx < count; idx++) + { + XRect rect = rects[idx]; + DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + } + + // ----- DrawRoundedRectangle ----------------------------------------------------------------- + + public void DrawRoundedRectangle(XPen pen, XBrush brush, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + XGraphicsPath path = new XGraphicsPath(); + path.AddRoundedRectangle(x, y, width, height, ellipseWidth, ellipseHeight); + DrawPath(pen, brush, path); + } + + // ----- DrawEllipse -------------------------------------------------------------------------- + + public void DrawEllipse(XPen pen, XBrush brush, double x, double y, double width, double height) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + Realize(pen, brush); + + // Useful information is here http://home.t-online.de/home/Robert.Rossmair/ellipse.htm (note: link was dead on November 2, 2015) + // or here http://www.whizkidtech.redprince.net/bezier/circle/ + // Deeper but more difficult: http://www.tinaja.com/cubic01.asp + XRect rect = new XRect(x, y, width, height); + double δx = rect.Width / 2; + double δy = rect.Height / 2; + double fx = δx * Const.κ; + double fy = δy * Const.κ; + double x0 = rect.X + δx; + double y0 = rect.Y + δy; + + // Approximate an ellipse by drawing four cubic splines. + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", x0 + δx, y0); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + x0 + δx, y0 + fy, x0 + fx, y0 + δy, x0, y0 + δy); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + x0 - fx, y0 + δy, x0 - δx, y0 + fy, x0 - δx, y0); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + x0 - δx, y0 - fy, x0 - fx, y0 - δy, x0, y0 - δy); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + x0 + fx, y0 - δy, x0 + δx, y0 - fy, x0 + δx, y0); + AppendStrokeFill(pen, brush, XFillMode.Winding, true); + } + + // ----- DrawPolygon -------------------------------------------------------------------------- + + public void DrawPolygon(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + Realize(pen, brush); + + int count = points.Length; + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx++) + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + + AppendStrokeFill(pen, brush, fillmode, true); + } + + // ----- DrawPie ------------------------------------------------------------------------------ + + public void DrawPie(XPen pen, XBrush brush, double x, double y, double width, double height, + double startAngle, double sweepAngle) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + Realize(pen, brush); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", x + width / 2, y + height / 2); + AppendPartialArc(x, y, width, height, startAngle, sweepAngle, PathStart.LineTo1st, new XMatrix()); + AppendStrokeFill(pen, brush, XFillMode.Alternate, true); + } + + // ----- DrawClosedCurve ---------------------------------------------------------------------- + + public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, double tension, XFillMode fillmode) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + int count = points.Length; + if (count == 0) + return; + if (count < 2) + throw new ArgumentException("Not enough points.", "points"); + + // Simply tried out. Not proofed why it is correct. + tension /= 3; + + Realize(pen, brush); + + const string format = Config.SignificantFigures4; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + if (count == 2) + { + // Just draw a line. + AppendCurveSegment(points[0], points[0], points[1], points[1], tension); + } + else + { + AppendCurveSegment(points[count - 1], points[0], points[1], points[2], tension); + for (int idx = 1; idx < count - 2; idx++) + AppendCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension); + AppendCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[0], tension); + AppendCurveSegment(points[count - 2], points[count - 1], points[0], points[1], tension); + } + AppendStrokeFill(pen, brush, fillmode, true); + } + + // ----- DrawPath ----------------------------------------------------------------------------- + + public void DrawPath(XPen pen, XBrush brush, XGraphicsPath path) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + if (pen == null && brush == null) + throw new ArgumentNullException("pen"); + +#if CORE + Realize(pen, brush); + AppendPath(path._corePath); + AppendStrokeFill(pen, brush, path.FillMode, false); +#endif +#if GDI && !WPF + Realize(pen, brush); + AppendPath(path._gdipPath); + AppendStrokeFill(pen, brush, path.FillMode, false); +#endif +#if WPF && !GDI + Realize(pen, brush); + AppendPath(path._pathGeometry); + AppendStrokeFill(pen, brush, path.FillMode, false); +#endif +#if WPF && GDI + Realize(pen, brush); + if (_gfx.TargetContext == XGraphicTargetContext.GDI) + AppendPath(path._gdipPath); + else + AppendPath(path._pathGeometry); + AppendStrokeFill(pen, brush, path.FillMode, false); +#endif +#if NETFX_CORE + Realize(pen, brush); + AppendPath(path._pathGeometry); + AppendStrokeFill(pen, brush, path.FillMode, false); +#endif + } + + // ----- DrawString --------------------------------------------------------------------------- + + public void DrawString(string s, XFont font, XBrush brush, XRect rect, XStringFormat format) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.DrawString }); // @PDF/UA + + double x = rect.X; + double y = rect.Y; + + double lineSpace = font.GetHeight(); + double cyAscent = lineSpace * font.CellAscent / font.CellSpace; + double cyDescent = lineSpace * font.CellDescent / font.CellSpace; + double width = _gfx.MeasureString(s, font).Width; + + bool italicSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.ItalicSimulation) != 0; + bool boldSimulation = (font.GlyphTypeface.StyleSimulations & XStyleSimulations.BoldSimulation) != 0; + bool strikeout = (font.Style & XFontStyle.Strikeout) != 0; + bool underline = (font.Style & XFontStyle.Underline) != 0; + + Realize(font, brush, boldSimulation ? 2 : 0); + + switch (format.Alignment) + { + case XStringAlignment.Near: + // nothing to do + break; + + case XStringAlignment.Center: + x += (rect.Width - width) / 2; + break; + + case XStringAlignment.Far: + x += rect.Width - width; + break; + } + if (Gfx.PageDirection == XPageDirection.Downwards) + { + switch (format.LineAlignment) + { + case XLineAlignment.Near: + y += cyAscent; + break; + + case XLineAlignment.Center: + // TODO: Use CapHeight. PDFlib also uses 3/4 of ascent + y += (cyAscent * 3 / 4) / 2 + rect.Height / 2; + break; + + case XLineAlignment.Far: + y += -cyDescent + rect.Height; + break; + + case XLineAlignment.BaseLine: + // Nothing to do. + break; + } + } + else + { + switch (format.LineAlignment) + { + case XLineAlignment.Near: + y += cyDescent; + break; + + case XLineAlignment.Center: + // TODO: Use CapHeight. PDFlib also uses 3/4 of ascent + y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2; + break; + + case XLineAlignment.Far: + y += -cyAscent + rect.Height; + break; + + case XLineAlignment.BaseLine: + // Nothing to do. + break; + } + } + + PdfFont realizedFont = _gfxState._realizedFont; + Debug.Assert(realizedFont != null); + realizedFont.AddChars(s); + + const string format2 = Config.SignificantFigures4; + OpenTypeDescriptor descriptor = realizedFont.FontDescriptor._descriptor; + + string text = null; + if (font.Unicode) + { + StringBuilder sb = new StringBuilder(); + bool isSymbolFont = descriptor.FontFace.cmap.symbol; + for (int idx = 0; idx < s.Length; idx++) + { + char ch = s[idx]; + if (isSymbolFont) + { + // Remap ch for symbol fonts. + ch = (char)(ch | (descriptor.FontFace.os2.usFirstCharIndex & 0xFF00)); // @@@ refactor + } + int glyphID = descriptor.CharCodeToGlyphIndex(ch); + sb.Append((char)glyphID); + } + s = sb.ToString(); + + byte[] bytes = PdfEncoders.RawUnicodeEncoding.GetBytes(s); + bytes = PdfEncoders.FormatStringLiteral(bytes, true, false, true, null); + text = PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); + } + else + { + byte[] bytes = PdfEncoders.WinAnsiEncoding.GetBytes(s); + text = PdfEncoders.ToStringLiteral(bytes, false, null); + } + + // Map absolute position to PDF world space. + XPoint pos = new XPoint(x, y); + pos = WorldToView(pos); + + double verticalOffset = 0; + if (boldSimulation) + { + // Adjust baseline in case of bold simulation??? + // No, because this would change the center of the glyphs. + //verticalOffset = font.Size * Const.BoldEmphasis / 2; + } + +#if ITALIC_SIMULATION + if (italicSimulation) + { + if (_gfxState.ItalicSimulationOn) + { + AdjustTdOffset(ref pos, verticalOffset, true); + AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} Td\n{2} Tj\n", pos.X, pos.Y, text); + } + else + { + // Italic simulation is done by skewing characters 20° to the right. + XMatrix m = new XMatrix(1, 0, Const.ItalicSkewAngleSinus, 1, pos.X, pos.Y); + AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} {2:" + format2 + "} {3:" + format2 + "} {4:" + format2 + "} {5:" + format2 + "} Tm\n{6} Tj\n", + m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY, text); + _gfxState.ItalicSimulationOn = true; + AdjustTdOffset(ref pos, verticalOffset, false); + } + } + else + { + if (_gfxState.ItalicSimulationOn) + { + XMatrix m = new XMatrix(1, 0, 0, 1, pos.X, pos.Y); + AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} {2:" + format2 + "} {3:" + format2 + "} {4:" + format2 + "} {5:" + format2 + "} Tm\n{6} Tj\n", + m.M11, m.M12, m.M21, m.M22, m.OffsetX, m.OffsetY, text); + _gfxState.ItalicSimulationOn = false; + AdjustTdOffset(ref pos, verticalOffset, false); + } + else + { + AdjustTdOffset(ref pos, verticalOffset, false); + AppendFormatArgs("{0:" + format2 + "} {1:" + format2 + "} Td {2} Tj\n", pos.X, pos.Y, text); + } + } +#else + AdjustTextMatrix(ref pos); + AppendFormat2("{0:" + format2 + "} {1:" + format2 + "} Td {2} Tj\n", pos.X, pos.Y, text); +#endif + if (underline) + { + double underlinePosition = lineSpace * realizedFont.FontDescriptor._descriptor.UnderlinePosition / font.CellSpace; + double underlineThickness = lineSpace * realizedFont.FontDescriptor._descriptor.UnderlineThickness / font.CellSpace; + //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness); + double underlineRectY = Gfx.PageDirection == XPageDirection.Downwards + ? y - underlinePosition + : y + underlinePosition - underlineThickness; + DrawRectangle(null, brush, x, underlineRectY, width, underlineThickness); + } + + if (strikeout) + { + double strikeoutPosition = lineSpace * realizedFont.FontDescriptor._descriptor.StrikeoutPosition / font.CellSpace; + double strikeoutSize = lineSpace * realizedFont.FontDescriptor._descriptor.StrikeoutSize / font.CellSpace; + //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); + double strikeoutRectY = Gfx.PageDirection == XPageDirection.Downwards + ? y - strikeoutPosition + : y + strikeoutPosition - strikeoutSize; + DrawRectangle(null, brush, x, strikeoutRectY, width, strikeoutSize); + } + } + + // ----- DrawImage ---------------------------------------------------------------------------- + + //public void DrawImage(Image image, Point point); + //public void DrawImage(Image image, PointF point); + //public void DrawImage(Image image, Point[] destPoints); + //public void DrawImage(Image image, PointF[] destPoints); + //public void DrawImage(Image image, Rectangle rect); + //public void DrawImage(Image image, RectangleF rect); + //public void DrawImage(Image image, int x, int y); + //public void DrawImage(Image image, float x, float y); + //public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, Rectangle destRect, Rectangle srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, int x, int y, Rectangle srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, float x, float y, RectangleF srcRect, GraphicsUnit srcUnit); + //public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr); + //public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr); + //public void DrawImage(Image image, int x, int y, int width, int height); + //public void DrawImage(Image image, float x, float y, float width, float height); + //public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback); + //public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback); + //public void DrawImage(Image image, Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, GraphicsUnit srcUnit); + //public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit); + //public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback, int callbackData); + //public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback, int callbackData); + //public void DrawImage(Image image, Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr); + //public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs); + //public void DrawImage(Image image, Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttr, DrawImageAbort callback); + //public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback); + //public void DrawImage(Image image, Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, GraphicsUnit srcUnit, ImageAttributes imageAttrs, DrawImageAbort callback, IntPtr callbackData); + //public void DrawImage(Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit srcUnit, ImageAttributes + + public void DrawImage(XImage image, double x, double y, double width, double height) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + const string format = Config.SignificantFigures4; + + string name = Realize(image); + if (!(image is XForm)) + { + if (_gfx.PageDirection == XPageDirection.Downwards) + { + AppendFormatImage("q {2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + x, y + height, width, height, name); + } + else + { + AppendFormatImage("q {2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + x, y, width, height, name); + } + } + else + { + BeginPage(); + + XForm form = (XForm)image; + form.Finish(); + + PdfFormXObject pdfForm = Owner.FormTable.GetForm(form); + + double cx = width / image.PointWidth; + double cy = height / image.PointHeight; + + if (cx != 0 && cy != 0) + { + XPdfForm xForm = image as XPdfForm; + // Reset colors in this graphics state. Usualy PDF imagages should set them, but in rare cases they don't which may result in changed colors inside the image. + var resetColor = xForm != null ? "\n0 g\n0 G\n" : " "; + + if (_gfx.PageDirection == XPageDirection.Downwards) + { + // If we have an XPdfForm, then we take the MediaBox into account. + double xDraw = x; + double yDraw = y; + if (xForm != null) + { + // Yes, it is an XPdfForm - adjust the position where the page will be drawn. + xDraw -= xForm.Page.MediaBox.X1; + yDraw += xForm.Page.MediaBox.Y1; + } + AppendFormatImage("q" + resetColor + "{2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm 100 Tz {4} Do Q\n", + xDraw, yDraw + height, cx, cy, name); + } + else + { + // TODO Translation for MediaBox. + AppendFormatImage("q" + resetColor + "{2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + x, y, cx, cy, name); + } + } + } + } + + // TODO: incomplete - srcRect not used + public void DrawImage(XImage image, XRect destRect, XRect srcRect, XGraphicsUnit srcUnit) + { + if (Owner._uaManager != null) + Owner.Events.OnPageGraphicsAction(Owner, new PageGraphicsEventArgs { Page = _page, Graphics = Gfx, ActionType = PageGraphicsActionType.Draw }); // @PDF/UA + + const string format = Config.SignificantFigures4; + + double x = destRect.X; + double y = destRect.Y; + double width = destRect.Width; + double height = destRect.Height; + + string name = Realize(image); + if (!(image is XForm)) + { + if (_gfx.PageDirection == XPageDirection.Downwards) + { + AppendFormatImage("q {2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do\nQ\n", + x, y + height, width, height, name); + } + else + { + AppendFormatImage("q {2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + x, y, width, height, name); + } + } + else + { + BeginPage(); + + XForm form = (XForm)image; + form.Finish(); + + PdfFormXObject pdfForm = Owner.FormTable.GetForm(form); + + double cx = width / image.PointWidth; + double cy = height / image.PointHeight; + + if (cx != 0 && cy != 0) + { + XPdfForm xForm = image as XPdfForm; + // Reset colors in this graphics state. Usualy PDF imagages should set them, but in rare cases they don't which may result in changed colors inside the image. + var resetColor = xForm != null ? "\n0 g\n0 G\n" : " "; + + if (_gfx.PageDirection == XPageDirection.Downwards) + { + double xDraw = x; + double yDraw = y; + if (xForm != null) + { + // Yes, it is an XPdfForm - adjust the position where the page will be drawn. + xDraw -= xForm.Page.MediaBox.X1; + yDraw += xForm.Page.MediaBox.Y1; + } + AppendFormatImage("q" + resetColor + "{2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + xDraw, yDraw + height, cx, cy, name); + } + else + { + // TODO Translation for MediaBox. + AppendFormatImage("q" + resetColor + "{2:" + format + "} 0 0 {3:" + format + "} {0:" + format + "} {1:" + format + "} cm {4} Do Q\n", + x, y, cx, cy, name); + } + } + } + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Save and Restore + + /// + /// Clones the current graphics state and push it on a stack. + /// + public void Save(XGraphicsState state) + { + // Before saving, the current transformation matrix must be completely realized. + BeginGraphicMode(); + RealizeTransform(); + // Associate the XGraphicsState with the current PdgGraphicsState. + _gfxState.InternalState = state.InternalState; + SaveState(); + } + + public void Restore(XGraphicsState state) + { + BeginGraphicMode(); + RestoreState(state.InternalState); + } + + public void BeginContainer(XGraphicsContainer container, XRect dstrect, XRect srcrect, XGraphicsUnit unit) + { + // Before saving, the current transformation matrix must be completely realized. + BeginGraphicMode(); + RealizeTransform(); + _gfxState.InternalState = container.InternalState; + SaveState(); + } + + public void EndContainer(XGraphicsContainer container) + { + BeginGraphicMode(); + RestoreState(container.InternalState); + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Transformation + + //public void SetPageTransform(XPageDirection direction, XPoint origion, XGraphicsUnit unit) + //{ + // if (_gfxStateStack.Count > 0) + // throw new InvalidOperationException("PageTransformation can be modified only when the graphics stack is empty."); + + // throw new NotImplementedException("SetPageTransform"); + //} + + public XMatrix Transform + { + get + { + if (_gfxState.UnrealizedCtm.IsIdentity) + return _gfxState.EffectiveCtm; + return _gfxState.UnrealizedCtm * _gfxState.RealizedCtm; + } + } + + public void AddTransform(XMatrix value, XMatrixOrder matrixOrder) + { + _gfxState.AddTransform(value, matrixOrder); + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Clipping + + public void SetClip(XGraphicsPath path, XCombineMode combineMode) + { + if (path == null) + throw new NotImplementedException("SetClip with no path."); + + // Ensure that the graphics state stack level is at least 2, because otherwise an error + // occurs when someone set the clip region before something was drawn. + if (_gfxState.Level < GraphicsStackLevelWorldSpace) + RealizeTransform(); // TODO: refactor this function + + if (combineMode == XCombineMode.Replace) + { + if (_clipLevel != 0) + { + if (_clipLevel != _gfxState.Level) + throw new NotImplementedException("Cannot set new clip region in an inner graphic state level."); + else + ResetClip(); + } + _clipLevel = _gfxState.Level; + } + else if (combineMode == XCombineMode.Intersect) + { + if (_clipLevel == 0) + _clipLevel = _gfxState.Level; + } + else + { + Debug.Assert(false, "Invalid XCombineMode in internal function."); + } + _gfxState.SetAndRealizeClipPath(path); + } + + /// + /// Sets the clip path empty. Only possible if graphic state level has the same value as it has when + /// the first time SetClip was invoked. + /// + public void ResetClip() + { + // No clip level means no clipping occurs and nothing is to do. + if (_clipLevel == 0) + return; + + // Only at the clipLevel the clipping can be reset. + if (_clipLevel != _gfxState.Level) + throw new NotImplementedException("Cannot reset clip region in an inner graphic state level."); + + // Must be in graphical mode before popping the graphics state. + BeginGraphicMode(); + + // Save InternalGraphicsState and transformation of the current graphical state. + InternalGraphicsState state = _gfxState.InternalState; + XMatrix ctm = _gfxState.EffectiveCtm; + // Empty clip path by switching back to the previous state. + RestoreState(); + SaveState(); + // Save internal state + _gfxState.InternalState = state; + // Restore CTM + // TODO: check rest of clip + //GfxState.Transform = ctm; + } + + /// + /// The nesting level of the PDF graphics state stack when the clip region was set to non empty. + /// Because of the way PDF is made the clip region can only be reset at this level. + /// + int _clipLevel; + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Miscellaneous + + /// + /// Writes a comment to the PDF content stream. May be useful for debugging purposes. + /// + public void WriteComment(string comment) + { + comment = comment.Replace("\n", "\n% "); + // TODO: Some more checks necessary? + Append("% " + comment + "\n"); + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Append to PDF stream + + /// + /// Appends one or up to five Bézier curves that interpolate the arc. + /// + void AppendPartialArc(double x, double y, double width, double height, double startAngle, double sweepAngle, PathStart pathStart, XMatrix matrix) + { + // Normalize the angles + double α = startAngle; + if (α < 0) + α = α + (1 + Math.Floor((Math.Abs(α) / 360))) * 360; + else if (α > 360) + α = α - Math.Floor(α / 360) * 360; + Debug.Assert(α >= 0 && α <= 360); + + double β = sweepAngle; + if (β < -360) + β = -360; + else if (β > 360) + β = 360; + + if (α == 0 && β < 0) + α = 360; + else if (α == 360 && β > 0) + α = 0; + + // Is it possible that the arc is small starts and ends in same quadrant? + bool smallAngle = Math.Abs(β) <= 90; + + β = α + β; + if (β < 0) + β = β + (1 + Math.Floor((Math.Abs(β) / 360))) * 360; + + bool clockwise = sweepAngle > 0; + int startQuadrant = Quadrant(α, true, clockwise); + int endQuadrant = Quadrant(β, false, clockwise); + + if (startQuadrant == endQuadrant && smallAngle) + AppendPartialArcQuadrant(x, y, width, height, α, β, pathStart, matrix); + else + { + int currentQuadrant = startQuadrant; + bool firstLoop = true; + do + { + if (currentQuadrant == startQuadrant && firstLoop) + { + double ξ = currentQuadrant * 90 + (clockwise ? 90 : 0); + AppendPartialArcQuadrant(x, y, width, height, α, ξ, pathStart, matrix); + } + else if (currentQuadrant == endQuadrant) + { + double ξ = currentQuadrant * 90 + (clockwise ? 0 : 90); + AppendPartialArcQuadrant(x, y, width, height, ξ, β, PathStart.Ignore1st, matrix); + } + else + { + double ξ1 = currentQuadrant * 90 + (clockwise ? 0 : 90); + double ξ2 = currentQuadrant * 90 + (clockwise ? 90 : 0); + AppendPartialArcQuadrant(x, y, width, height, ξ1, ξ2, PathStart.Ignore1st, matrix); + } + + // Don't stop immediately if arc is greater than 270 degrees + if (currentQuadrant == endQuadrant && smallAngle) + break; + + smallAngle = true; + + if (clockwise) + currentQuadrant = currentQuadrant == 3 ? 0 : currentQuadrant + 1; + else + currentQuadrant = currentQuadrant == 0 ? 3 : currentQuadrant - 1; + + firstLoop = false; + } while (true); + } + } + + /// + /// Gets the quadrant (0 through 3) of the specified angle. If the angle lies on an edge + /// (0, 90, 180, etc.) the result depends on the details how the angle is used. + /// + int Quadrant(double φ, bool start, bool clockwise) + { + Debug.Assert(φ >= 0); + if (φ > 360) + φ = φ - Math.Floor(φ / 360) * 360; + + int quadrant = (int)(φ / 90); + if (quadrant * 90 == φ) + { + if ((start && !clockwise) || (!start && clockwise)) + quadrant = quadrant == 0 ? 3 : quadrant - 1; + } + else + quadrant = clockwise ? ((int)Math.Floor(φ / 90)) % 4 : (int)Math.Floor(φ / 90); + return quadrant; + } + + /// + /// Appends a Bézier curve for an arc within a quadrant. + /// + void AppendPartialArcQuadrant(double x, double y, double width, double height, double α, double β, PathStart pathStart, XMatrix matrix) + { + Debug.Assert(α >= 0 && α <= 360); + Debug.Assert(β >= 0); + if (β > 360) + β = β - Math.Floor(β / 360) * 360; + Debug.Assert(Math.Abs(α - β) <= 90); + + // Scanling factor + double δx = width / 2; + double δy = height / 2; + + // Center of ellipse + double x0 = x + δx; + double y0 = y + δy; + + // We have the following quarters: + // | + // 2 | 3 + // ----+----- + // 1 | 0 + // | + // If the angles lie in quarter 2 or 3, their values are subtracted by 180 and the + // resulting curve is reflected at the center. This algorithm works as expected (simply tried out). + // There may be a mathematically more elegant solution... + bool reflect = false; + if (α >= 180 && β >= 180) + { + α -= 180; + β -= 180; + reflect = true; + } + + double sinα, sinβ; + if (width == height) + { + // Circular arc needs no correction. + α = α * Calc.Deg2Rad; + β = β * Calc.Deg2Rad; + } + else + { + // Elliptic arc needs the angles to be adjusted such that the scaling transformation is compensated. + α = α * Calc.Deg2Rad; + sinα = Math.Sin(α); + if (Math.Abs(sinα) > 1E-10) + α = Math.PI / 2 - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); + β = β * Calc.Deg2Rad; + sinβ = Math.Sin(β); + if (Math.Abs(sinβ) > 1E-10) + β = Math.PI / 2 - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); + } + + double κ = 4 * (1 - Math.Cos((α - β) / 2)) / (3 * Math.Sin((β - α) / 2)); + sinα = Math.Sin(α); + double cosα = Math.Cos(α); + sinβ = Math.Sin(β); + double cosβ = Math.Cos(β); + + const string format = Config.SignificantFigures3; + XPoint pt1, pt2, pt3; + if (!reflect) + { + // Calculation for quarter 0 and 1 + switch (pathStart) + { + case PathStart.MoveTo1st: + pt1 = matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα)); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", pt1.X, pt1.Y); + break; + + case PathStart.LineTo1st: + pt1 = matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα)); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", pt1.X, pt1.Y); + break; + + case PathStart.Ignore1st: + break; + } + pt1 = matrix.Transform(new XPoint(x0 + δx * (cosα - κ * sinα), y0 + δy * (sinα + κ * cosα))); + pt2 = matrix.Transform(new XPoint(x0 + δx * (cosβ + κ * sinβ), y0 + δy * (sinβ - κ * cosβ))); + pt3 = matrix.Transform(new XPoint(x0 + δx * cosβ, y0 + δy * sinβ)); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y); + } + else + { + // Calculation for quarter 2 and 3. + switch (pathStart) + { + case PathStart.MoveTo1st: + pt1 = matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα)); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", pt1.X, pt1.Y); + break; + + case PathStart.LineTo1st: + pt1 = matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα)); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", pt1.X, pt1.Y); + break; + + case PathStart.Ignore1st: + break; + } + pt1 = matrix.Transform(new XPoint(x0 - δx * (cosα - κ * sinα), y0 - δy * (sinα + κ * cosα))); + pt2 = matrix.Transform(new XPoint(x0 - δx * (cosβ + κ * sinβ), y0 - δy * (sinβ - κ * cosβ))); + pt3 = matrix.Transform(new XPoint(x0 - δx * cosβ, y0 - δy * sinβ)); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y); + } + } + +#if WPF || NETFX_CORE + void AppendPartialArc(SysPoint point1, SysPoint point2, double rotationAngle, + SysSize size, bool isLargeArc, SweepDirection sweepDirection, PathStart pathStart) + { + const string format = Config.SignificantFigures4; + + Debug.Assert(pathStart == PathStart.Ignore1st); + + int pieces; + PointCollection points = GeometryHelper.ArcToBezier(point1.X, point1.Y, size.Width, size.Height, rotationAngle, isLargeArc, + sweepDirection == SweepDirection.Clockwise, point2.X, point2.Y, out pieces); + + int count = points.Count; + int start = count % 3 == 1 ? 1 : 0; + if (start == 1) + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[0].X, points[0].Y); + for (int idx = start; idx < count; idx += 3) + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + points[idx].X, points[idx].Y, + points[idx + 1].X, points[idx + 1].Y, + points[idx + 2].X, points[idx + 2].Y); + } +#endif + + /// + /// Appends a Bézier curve for a cardinal spline through pt1 and pt2. + /// + void AppendCurveSegment(XPoint pt0, XPoint pt1, XPoint pt2, XPoint pt3, double tension3) + { + const string format = Config.SignificantFigures4; + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + pt1.X + tension3 * (pt2.X - pt0.X), pt1.Y + tension3 * (pt2.Y - pt0.Y), + pt2.X - tension3 * (pt3.X - pt1.X), pt2.Y - tension3 * (pt3.Y - pt1.Y), + pt2.X, pt2.Y); + } + +#if CORE_ + /// + /// Appends the content of a GraphicsPath object. + /// + internal void AppendPath(GraphicsPath path) + { + int count = path.PointCount; + if (count == 0) + return; + PointF[] points = path.PathPoints; + Byte[] types = path.PathTypes; + + for (int idx = 0; idx < count; idx++) + { + // From GDI+ documentation: + const byte PathPointTypeStart = 0; // move + const byte PathPointTypeLine = 1; // line + const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) + const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). + //const byte PathPointTypeDashMode = 0x10; // currently in dash mode. + //const byte PathPointTypePathMarker = 0x20; // a marker for the path. + const byte PathPointTypeCloseSubpath = 0x80; // closed flag + + byte type = types[idx]; + switch (type & PathPointTypePathTypeMask) + { + case PathPointTypeStart: + //PDF_moveto(pdf, points[idx].X, points[idx].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} m\n", points[idx].X, points[idx].Y); + break; + + case PathPointTypeLine: + //PDF_lineto(pdf, points[idx].X, points[idx].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + if ((type & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + + case PathPointTypeBezier: + Debug.Assert(idx + 2 < count); + //PDF_curveto(pdf, points[idx].X, points[idx].Y, + // points[idx + 1].X, points[idx + 1].Y, + // points[idx + 2].X, points[idx + 2].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", points[idx].X, points[idx].Y, + points[++idx].X, points[idx].Y, points[++idx].X, points[idx].Y); + if ((types[idx] & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + } + } + } +#endif + +#if CORE + /// + /// Appends the content of a GraphicsPath object. + /// + internal void AppendPath(CoreGraphicsPath path) + { + AppendPath(path.PathPoints, path.PathTypes); + //XPoint[] points = path.PathPoints; + //Byte[] types = path.PathTypes; + + //int count = points.Length; + //if (count == 0) + // return; + + //for (int idx = 0; idx < count; idx++) + //{ + // // From GDI+ documentation: + // const byte PathPointTypeStart = 0; // move + // const byte PathPointTypeLine = 1; // line + // const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) + // const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). + // //const byte PathPointTypeDashMode = 0x10; // currently in dash mode. + // //const byte PathPointTypePathMarker = 0x20; // a marker for the path. + // const byte PathPointTypeCloseSubpath = 0x80; // closed flag + + // byte type = types[idx]; + // switch (type & PathPointTypePathTypeMask) + // { + // case PathPointTypeStart: + // //PDF_moveto(pdf, points[idx].X, points[idx].Y); + // AppendFormat("{0:" + format + "} {1:" + format + "} m\n", points[idx].X, points[idx].Y); + // break; + + // case PathPointTypeLine: + // //PDF_lineto(pdf, points[idx].X, points[idx].Y); + // AppendFormat("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + // if ((type & PathPointTypeCloseSubpath) != 0) + // Append("h\n"); + // break; + + // case PathPointTypeBezier: + // Debug.Assert(idx + 2 < count); + // //PDF_curveto(pdf, points[idx].X, points[idx].Y, + // // points[idx + 1].X, points[idx + 1].Y, + // // points[idx + 2].X, points[idx + 2].Y); + // AppendFormat("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", points[idx].X, points[idx].Y, + // points[++idx].X, points[idx].Y, points[++idx].X, points[idx].Y); + // if ((types[idx] & PathPointTypeCloseSubpath) != 0) + // Append("h\n"); + // break; + // } + //} + } +#endif + +#if GDI + /// + /// Appends the content of a GraphicsPath object. + /// + internal void AppendPath(GraphicsPath path) + { +#if true + AppendPath(XGraphics.MakeXPointArray(path.PathPoints, 0, path.PathPoints.Length), path.PathTypes); +#else + int count = path.PointCount; + if (count == 0) + return; + PointF[] points = path.PathPoints; + Byte[] types = path.PathTypes; + + for (int idx = 0; idx < count; idx++) + { + // From GDI+ documentation: + const byte PathPointTypeStart = 0; // move + const byte PathPointTypeLine = 1; // line + const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) + const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). + //const byte PathPointTypeDashMode = 0x10; // currently in dash mode. + //const byte PathPointTypePathMarker = 0x20; // a marker for the path. + const byte PathPointTypeCloseSubpath = 0x80; // closed flag + + byte type = types[idx]; + switch (type & PathPointTypePathTypeMask) + { + case PathPointTypeStart: + //PDF_moveto(pdf, points[idx].X, points[idx].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} m\n", points[idx].X, points[idx].Y); + break; + + case PathPointTypeLine: + //PDF_lineto(pdf, points[idx].X, points[idx].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + if ((type & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + + case PathPointTypeBezier: + Debug.Assert(idx + 2 < count); + //PDF_curveto(pdf, points[idx].X, points[idx].Y, + // points[idx + 1].X, points[idx + 1].Y, + // points[idx + 2].X, points[idx + 2].Y); + AppendFormat("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", points[idx].X, points[idx].Y, + points[++idx].X, points[idx].Y, points[++idx].X, points[idx].Y); + if ((types[idx] & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + } + } +#endif + } +#endif + +#if CORE || GDI + void AppendPath(XPoint[] points, Byte[] types) + { + const string format = Config.SignificantFigures4; + int count = points.Length; + if (count == 0) + return; + + for (int idx = 0; idx < count; idx++) + { + // ReSharper disable InconsistentNaming + // From GDI+ documentation: + const byte PathPointTypeStart = 0; // move + const byte PathPointTypeLine = 1; // line + const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) + const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). + //const byte PathPointTypeDashMode = 0x10; // currently in dash mode. + //const byte PathPointTypePathMarker = 0x20; // a marker for the path. + const byte PathPointTypeCloseSubpath = 0x80; // closed flag + // ReSharper restore InconsistentNaming + + byte type = types[idx]; + switch (type & PathPointTypePathTypeMask) + { + case PathPointTypeStart: + //PDF_moveto(pdf, points[idx].X, points[idx].Y); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", points[idx].X, points[idx].Y); + break; + + case PathPointTypeLine: + //PDF_lineto(pdf, points[idx].X, points[idx].Y); + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", points[idx].X, points[idx].Y); + if ((type & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + + case PathPointTypeBezier: + Debug.Assert(idx + 2 < count); + //PDF_curveto(pdf, points[idx].X, points[idx].Y, + // points[idx + 1].X, points[idx + 1].Y, + // points[idx + 2].X, points[idx + 2].Y); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", points[idx].X, points[idx].Y, + points[++idx].X, points[idx].Y, points[++idx].X, points[idx].Y); + if ((types[idx] & PathPointTypeCloseSubpath) != 0) + Append("h\n"); + break; + } + } + } +#endif + +#if WPF || NETFX_CORE + /// + /// Appends the content of a PathGeometry object. + /// + internal void AppendPath(PathGeometry geometry) + { + const string format = Config.SignificantFigures4; + + foreach (PathFigure figure in geometry.Figures) + { +#if DEBUG + //#warning For DdlGBE_Chart_Layout (WPF) execution stucks at this Assertion. + // The empty Figure is added via XGraphicsPath.CurrentPathFigure Getter. + // Some methods like XGraphicsPath.AddRectangle() or AddLine() use this emtpy Figure to add Segments, others like AddEllipse() don't. + // Here, _pathGeometry.AddGeometry() of course ignores this first Figure and adds a second. + // Encapsulate relevant Add methods to delete a first emty Figure or move the Addition of an first empty Figure to a GetOrCreateCurrentPathFigure() or simply remove Assertion? + // Look for: + // MAOS4STLA: CurrentPathFigure. + + + if (figure.Segments.Count == 0) + 42.GetType(); + Debug.Assert(figure.Segments.Count > 0); +#endif + // Skip the Move if the segment is empty. Workaround for empty segments. Empty segments should not occur (see Debug.Assert above). + if (figure.Segments.Count > 0) + { + // Move to start point. + SysPoint currentPoint = figure.StartPoint; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} m\n", currentPoint.X, currentPoint.Y); + + foreach (PathSegment segment in figure.Segments) + { + Type type = segment.GetType(); + if (type == typeof(LineSegment)) + { + // Draw a single line. + SysPoint point = ((LineSegment)segment).Point; + currentPoint = point; + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", point.X, point.Y); + } + else if (type == typeof(PolyLineSegment)) + { + // Draw connected lines. + PointCollection points = ((PolyLineSegment)segment).Points; + foreach (SysPoint point in points) + { + currentPoint = point; // I forced myself not to optimize this assignment. + AppendFormatPoint("{0:" + format + "} {1:" + format + "} l\n", point.X, point.Y); + } + } + else if (type == typeof(BezierSegment)) + { + // Draw Bézier curve. + BezierSegment seg = (BezierSegment)segment; + SysPoint point1 = seg.Point1; + SysPoint point2 = seg.Point2; + SysPoint point3 = seg.Point3; + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + point1.X, point1.Y, point2.X, point2.Y, point3.X, point3.Y); + currentPoint = point3; + } + else if (type == typeof(PolyBezierSegment)) + { + // Draw connected Bézier curves. + PointCollection points = ((PolyBezierSegment)segment).Points; + int count = points.Count; + if (count > 0) + { + Debug.Assert(count % 3 == 0, "Number of Points in PolyBezierSegment are not a multiple of 3."); + for (int idx = 0; idx < count - 2; idx += 3) + { + SysPoint point1 = points[idx]; + SysPoint point2 = points[idx + 1]; + SysPoint point3 = points[idx + 2]; + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} c\n", + point1.X, point1.Y, point2.X, point2.Y, point3.X, point3.Y); + } + currentPoint = points[count - 1]; + } + } + else if (type == typeof(ArcSegment)) + { + // Draw arc. + ArcSegment seg = (ArcSegment)segment; + AppendPartialArc(currentPoint, seg.Point, seg.RotationAngle, seg.Size, seg.IsLargeArc, seg.SweepDirection, PathStart.Ignore1st); + currentPoint = seg.Point; + } + else if (type == typeof(QuadraticBezierSegment)) + { + QuadraticBezierSegment seg = (QuadraticBezierSegment)segment; + currentPoint = seg.Point2; + // TODOWPF: Undone because XGraphics has no such curve type + throw new NotImplementedException("AppendPath with QuadraticBezierSegment."); + } + else if (type == typeof(PolyQuadraticBezierSegment)) + { + PolyQuadraticBezierSegment seg = (PolyQuadraticBezierSegment)segment; + currentPoint = seg.Points[seg.Points.Count - 1]; + // TODOWPF: Undone because XGraphics has no such curve type + throw new NotImplementedException("AppendPath with PolyQuadraticBezierSegment."); + } + } + if (figure.IsClosed) + Append("h\n"); + } + } + } +#endif + + internal void Append(string value) + { + _content.Append(value); + } + + internal void AppendFormatArgs(string format, params object[] args) + { + _content.AppendFormat(CultureInfo.InvariantCulture, format, args); +#if DEBUG + string dummy = _content.ToString(); + dummy = dummy.Substring(Math.Max(0, dummy.Length - 100)); + dummy.GetType(); +#endif + } + + internal void AppendFormatString(string format, string s) + { + _content.AppendFormat(CultureInfo.InvariantCulture, format, s); + } + + internal void AppendFormatFont(string format, string s, double d) + { + _content.AppendFormat(CultureInfo.InvariantCulture, format, s, d); + } + + internal void AppendFormatInt(string format, int n) + { + _content.AppendFormat(CultureInfo.InvariantCulture, format, n); + } + + internal void AppendFormatDouble(string format, double d) + { + _content.AppendFormat(CultureInfo.InvariantCulture, format, d); + } + + internal void AppendFormatPoint(string format, double x, double y) + { + XPoint result = WorldToView(new XPoint(x, y)); + _content.AppendFormat(CultureInfo.InvariantCulture, format, result.X, result.Y); + } + + internal void AppendFormatRect(string format, double x, double y, double width, double height) + { + XPoint point1 = WorldToView(new XPoint(x, y)); + _content.AppendFormat(CultureInfo.InvariantCulture, format, point1.X, point1.Y, width, height); + } + + internal void AppendFormat3Points(string format, double x1, double y1, double x2, double y2, double x3, double y3) + { + XPoint point1 = WorldToView(new XPoint(x1, y1)); + XPoint point2 = WorldToView(new XPoint(x2, y2)); + XPoint point3 = WorldToView(new XPoint(x3, y3)); + _content.AppendFormat(CultureInfo.InvariantCulture, format, point1.X, point1.Y, point2.X, point2.Y, point3.X, point3.Y); + } + + internal void AppendFormat(string format, XPoint point) + { + XPoint result = WorldToView(point); + _content.AppendFormat(CultureInfo.InvariantCulture, format, result.X, result.Y); + } + + internal void AppendFormat(string format, double x, double y, string s) + { + XPoint result = WorldToView(new XPoint(x, y)); + _content.AppendFormat(CultureInfo.InvariantCulture, format, result.X, result.Y, s); + } + + internal void AppendFormatImage(string format, double x, double y, double width, double height, string name) + { + XPoint result = WorldToView(new XPoint(x, y)); + _content.AppendFormat(CultureInfo.InvariantCulture, format, result.X, result.Y, width, height, name); + } + + void AppendStrokeFill(XPen pen, XBrush brush, XFillMode fillMode, bool closePath) + { + if (closePath) + _content.Append("h "); + + if (fillMode == XFillMode.Winding) + { + if (pen != null && brush != null) + _content.Append("B\n"); + else if (pen != null) + _content.Append("S\n"); + else + _content.Append("f\n"); + } + else + { + if (pen != null && brush != null) + _content.Append("B*\n"); + else if (pen != null) + _content.Append("S\n"); + else + _content.Append("f*\n"); + } + } + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Realizing graphical state + + /// + /// Initializes the default view transformation, i.e. the transformation from the user page + /// space to the PDF page space. + /// + internal void BeginPage() // @PDF/UA + { + if (_gfxState.Level == GraphicsStackLevelInitial) + { + // TODO: Is PageOriging and PageScale (== Viewport) useful? Or just public DefaultViewMatrix (like Presentation Manager has had) + // May be a BeginContainer(windows, viewport) is useful for userer that are not familar with maxtrix transformations. + + // Flip page horizontally and mirror text. + + // PDF uses a standard right-handed Cartesian coordinate system with the y axis directed up + // and the rotation counterclockwise. Windows uses the opposite convertion with y axis + // directed down and rotation clockwise. When I started with PDFsharp I flipped pages horizontally + // and then mirrored text to compensate the effect that the fipping turns text upside down. + // I found this technique during analysis of PDF documents generated with PDFlib. Unfortunately + // this technique leads to several problems with programms that compose or view PDF documents + // generated with PDFsharp. + // In PDFsharp 1.4 I implement a revised technique that does not need text mirroring any more. + + DefaultViewMatrix = new XMatrix(); + if (_gfx.PageDirection == XPageDirection.Downwards) + { + // Take TrimBox into account. + PageHeightPt = Size.Height; + XPoint trimOffset = new XPoint(); + if (_page != null && _page.TrimMargins.AreSet) + { + PageHeightPt += _page.TrimMargins.Top.Point + _page.TrimMargins.Bottom.Point; + trimOffset = new XPoint(_page.TrimMargins.Left.Point, _page.TrimMargins.Top.Point); + } + + // Scale with page units. + switch (_gfx.PageUnit) + { + case XGraphicsUnit.Point: + // Factor is 1. + // DefaultViewMatrix.ScalePrepend(XUnit.PointFactor); + break; + + case XGraphicsUnit.Presentation: + DefaultViewMatrix.ScalePrepend(XUnit.PresentationFactor); + break; + + case XGraphicsUnit.Inch: + DefaultViewMatrix.ScalePrepend(XUnit.InchFactor); + break; + + case XGraphicsUnit.Millimeter: + DefaultViewMatrix.ScalePrepend(XUnit.MillimeterFactor); + break; + + case XGraphicsUnit.Centimeter: + DefaultViewMatrix.ScalePrepend(XUnit.CentimeterFactor); + break; + } + + if (trimOffset != new XPoint()) + { + Debug.Assert(_gfx.PageUnit == XGraphicsUnit.Point, "With TrimMargins set the page units must be Point. Ohter cases nyi."); + DefaultViewMatrix.TranslatePrepend(trimOffset.X, -trimOffset.Y); + } + + // Save initial graphic state. + SaveState(); + + // Set default page transformation, if any. + if (!DefaultViewMatrix.IsIdentity) + { + Debug.Assert(_gfxState.RealizedCtm.IsIdentity); + //_gfxState.RealizedCtm = DefaultViewMatrix; + const string format = Config.SignificantFigures7; + double[] cm = DefaultViewMatrix.GetElements(); + AppendFormatArgs("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", + cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); + } + + // Set page transformation + //double[] cm = DefaultViewMatrix.GetElements(); + //AppendFormat("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", + // cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); + } + else + { + // Scale with page units. + switch (_gfx.PageUnit) + { + case XGraphicsUnit.Point: + // Factor is 1. + // DefaultViewMatrix.ScalePrepend(XUnit.PointFactor); + break; + + case XGraphicsUnit.Presentation: + DefaultViewMatrix.ScalePrepend(XUnit.PresentationFactor); + break; + + case XGraphicsUnit.Inch: + DefaultViewMatrix.ScalePrepend(XUnit.InchFactor); + break; + + case XGraphicsUnit.Millimeter: + DefaultViewMatrix.ScalePrepend(XUnit.MillimeterFactor); + break; + + case XGraphicsUnit.Centimeter: + DefaultViewMatrix.ScalePrepend(XUnit.CentimeterFactor); + break; + } + + // Save initial graphic state. + SaveState(); + // Set page transformation. + const string format = Config.SignificantFigures7; + double[] cm = DefaultViewMatrix.GetElements(); + AppendFormat3Points("{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "} cm ", + cm[0], cm[1], cm[2], cm[3], cm[4], cm[5]); + } + } + } + + /// + /// Ends the content stream, i.e. ends the text mode and balances the graphic state stack. + /// + void EndPage() + { + if (_streamMode == StreamMode.Text) + { + _content.Append("ET\n"); + _streamMode = StreamMode.Graphic; + } + + while (_gfxStateStack.Count != 0) + RestoreState(); + } + + /// + /// Begins the graphic mode (i.e. ends the text mode). + /// + internal void BeginGraphicMode() + { + if (_streamMode != StreamMode.Graphic) + { + if (_streamMode == StreamMode.Text) + _content.Append("ET\n"); + + _streamMode = StreamMode.Graphic; + } + } + + /// + /// Begins the graphic mode (i.e. ends the text mode). + /// + internal void BeginTextMode() + { + if (_streamMode != StreamMode.Text) + { + _streamMode = StreamMode.Text; + _content.Append("BT\n"); + // Text matrix is empty after BT + _gfxState.RealizedTextPosition = new XPoint(); + _gfxState.ItalicSimulationOn = false; + } + } + + internal bool IsInTextMode() // @PDF/UA + { + return _streamMode == StreamMode.Text; + } + + StreamMode _streamMode; + + /// + /// Makes the specified pen and brush to the current graphics objects. + /// + private void Realize(XPen pen, XBrush brush) + { + BeginPage(); + BeginGraphicMode(); + RealizeTransform(); + + if (pen != null) + _gfxState.RealizePen(pen, _colorMode); // page.document.Options.ColorMode); + + if (brush != null) + { + // Render mode is 0 except for bold simulation. + _gfxState.RealizeBrush(brush, _colorMode, 0, 0); // page.document.Options.ColorMode); + } + } + + /// + /// Makes the specified pen to the current graphics object. + /// + void Realize(XPen pen) + { + Realize(pen, null); + } + + /// + /// Makes the specified brush to the current graphics object. + /// + void Realize(XBrush brush) + { + Realize(null, brush); + } + + /// + /// Makes the specified font and brush to the current graphics objects. + /// + void Realize(XFont font, XBrush brush, int renderingMode) + { + BeginPage(); + RealizeTransform(); + BeginTextMode(); + _gfxState.RealizeFont(font, brush, renderingMode); + } + + /// + /// PDFsharp uses the Td operator to set the text position. Td just sets the offset of the text matrix + /// and produces lesser code as Tm. + /// + /// The absolute text position. + /// The dy. + /// true if skewing for italic simulation is currently on. + void AdjustTdOffset(ref XPoint pos, double dy, bool adjustSkew) + { + pos.Y += dy; + // Reference: TABLE 5.5 Text-positioning operators / Page 406 + XPoint posSave = pos; + // Map from absolute to relative position. + pos = pos - new XVector(_gfxState.RealizedTextPosition.X, _gfxState.RealizedTextPosition.Y); + if (adjustSkew) + { + // In case that italic simulation is on X must be adjusted according to Y offset. Weird but works :-) + pos.X -= Const.ItalicSkewAngleSinus * pos.Y; + } + _gfxState.RealizedTextPosition = posSave; + } + + /// + /// Makes the specified image to the current graphics object. + /// + string Realize(XImage image) + { + BeginPage(); + BeginGraphicMode(); + RealizeTransform(); + + // The transparency set for a brush also applies to images. Set opacity to 100% so image will be drawn without transparency. + _gfxState.RealizeNonStrokeTransparency(1, _colorMode); + + XForm form = image as XForm; + return form != null ? GetFormName(form) : GetImageName(image); + } + + /// + /// Realizes the current transformation matrix, if necessary. + /// + void RealizeTransform() + { + BeginPage(); + + if (_gfxState.Level == GraphicsStackLevelPageSpace) + { + BeginGraphicMode(); + SaveState(); + } + + //if (gfxState.MustRealizeCtm) + if (!_gfxState.UnrealizedCtm.IsIdentity) + { + BeginGraphicMode(); + _gfxState.RealizeCtm(); + } + } + + /// + /// Convert a point from Windows world space to PDF world space. + /// + internal XPoint WorldToView(XPoint point) + { + // If EffectiveCtm is not yet realized InverseEffectiveCtm is invalid. + Debug.Assert(_gfxState.UnrealizedCtm.IsIdentity, "Somewhere a RealizeTransform is missing."); +#if true + // See in #else case why this is correct. + XPoint pt = _gfxState.WorldTransform.Transform(point); + return _gfxState.InverseEffectiveCtm.Transform(new XPoint(pt.X, PageHeightPt / DefaultViewMatrix.M22 - pt.Y)); +#else + // Get inverted PDF world transform matrix. + XMatrix invers = _gfxState.EffectiveCtm; + invers.Invert(); + + // Apply transform in Windows world space. + XPoint pt1 = _gfxState.WorldTransform.Transform(point); +#if true + // Do the transformation (see #else case) in one step. + XPoint pt2 = new XPoint(pt1.X, PageHeightPt / DefaultViewMatrix.M22 - pt1.Y); +#else + // Replicable version + + // Apply default transformation. + pt1.X = pt1.X * DefaultViewMatrix.M11; + pt1.Y = pt1.Y * DefaultViewMatrix.M22; + + // Convert from Windows space to PDF space. + XPoint pt2 = new XPoint(pt1.X, PageHeightPt - pt1.Y); + + pt2.X = pt2.X / DefaultViewMatrix.M11; + pt2.Y = pt2.Y / DefaultViewMatrix.M22; +#endif + XPoint pt3 = invers.Transform(pt2); + return pt3; +#endif + } + #endregion + +#if GDI + [Conditional("DEBUG")] + void DumpPathData(PathData pathData) + { + XPoint[] points = new XPoint[pathData.Points.Length]; + for (int i = 0; i < points.Length; i++) + points[i] = new XPoint(pathData.Points[i].X, pathData.Points[i].Y); + + DumpPathData(points, pathData.Types); + } +#endif +#if CORE || GDI + [Conditional("DEBUG")] + void DumpPathData(XPoint[] points, byte[] types) + { + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + string info = PdfEncoders.Format("{0:X} {1:####0.000} {2:####0.000}", types[idx], points[idx].X, points[idx].Y); + Debug.WriteLine(info, "PathData"); + } + } +#endif + + /// + /// Gets the owning PdfDocument of this page or form. + /// + internal PdfDocument Owner + { + get + { + if (_page != null) + return _page.Owner; + return _form.Owner; + } + } + + internal XGraphics Gfx + { + get { return _gfx; } + } + + /// + /// Gets the PdfResources of this page or form. + /// + internal PdfResources Resources + { + get + { + if (_page != null) + return _page.Resources; + return _form.Resources; + } + } + + /// + /// Gets the size of this page or form. + /// + internal XSize Size + { + get + { + if (_page != null) + return new XSize(_page.Width, _page.Height); + return _form.Size; + } + } + + /// + /// Gets the resource name of the specified font within this page or form. + /// + internal string GetFontName(XFont font, out PdfFont pdfFont) + { + if (_page != null) + return _page.GetFontName(font, out pdfFont); + return _form.GetFontName(font, out pdfFont); + } + + /// + /// Gets the resource name of the specified image within this page or form. + /// + internal string GetImageName(XImage image) + { + if (_page != null) + return _page.GetImageName(image); + return _form.GetImageName(image); + } + + /// + /// Gets the resource name of the specified form within this page or form. + /// + internal string GetFormName(XForm form) + { + if (_page != null) + return _page.GetFormName(form); + return _form.GetFormName(form); + } + + internal PdfPage _page; + internal XForm _form; + internal PdfColorMode _colorMode; + XGraphicsPdfPageOptions _options; + XGraphics _gfx; + internal readonly StringBuilder _content; + + /// + /// The q/Q nesting level is 0. + /// + const int GraphicsStackLevelInitial = 0; + + /// + /// The q/Q nesting level is 1. + /// + const int GraphicsStackLevelPageSpace = 1; + + /// + /// The q/Q nesting level is 2. + /// + const int GraphicsStackLevelWorldSpace = 2; + + #region PDF Graphics State + + /// + /// Saves the current graphical state. + /// + void SaveState() + { + Debug.Assert(_streamMode == StreamMode.Graphic, "Cannot save state in text mode."); + + _gfxStateStack.Push(_gfxState); + _gfxState = _gfxState.Clone(); + _gfxState.Level = _gfxStateStack.Count; + Append("q\n"); + } + + /// + /// Restores the previous graphical state. + /// + void RestoreState() + { + Debug.Assert(_streamMode == StreamMode.Graphic, "Cannot restore state in text mode."); + + _gfxState = _gfxStateStack.Pop(); + Append("Q\n"); + } + + PdfGraphicsState RestoreState(InternalGraphicsState state) + { + int count = 1; + PdfGraphicsState top = _gfxStateStack.Pop(); + while (top.InternalState != state) + { + Append("Q\n"); + count++; + top = _gfxStateStack.Pop(); + } + Append("Q\n"); + _gfxState = top; + return top; + } + + /// + /// The current graphical state. + /// + PdfGraphicsState _gfxState; + + /// + /// The graphical state stack. + /// + readonly Stack _gfxStateStack = new Stack(); + + #endregion + + /// + /// The height of the PDF page in point including the trim box. + /// + public double PageHeightPt; + + /// + /// The final transformation from the world space to the default page space. + /// + public XMatrix DefaultViewMatrix; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/DirtyFlags.cs b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/DirtyFlags.cs new file mode 100644 index 00000000..2409cb89 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/DirtyFlags.cs @@ -0,0 +1,44 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing.Pdf +{ + [Flags] + enum DirtyFlags + { + Ctm = 0x00000001, + ClipPath = 0x00000002, + LineWidth = 0x00000010, + LineJoin = 0x00000020, + MiterLimit = 0x00000040, + StrokeFill = 0x00000070, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/StreamMode.cs b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/StreamMode.cs new file mode 100644 index 00000000..8e8df55f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing.Pdf/enums/StreamMode.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing.Pdf +{ + /// + /// Indicates whether we are within a BT/ET block. + /// + enum StreamMode + { + /// + /// Graphic mode. This is default. + /// + Graphic, + + /// + /// Text mode. + /// + Text, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/CoreGraphicsPath.cs b/src/PDFsharp/src/PdfSharp/Drawing/CoreGraphicsPath.cs new file mode 100644 index 00000000..ae3f4ba7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/CoreGraphicsPath.cs @@ -0,0 +1,302 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Internal; + +namespace PdfSharp.Drawing +{ + /// + /// Represents a graphics path that uses the same notation as GDI+. + /// + internal class CoreGraphicsPath + { + // Same values as GDI+ uses. + const byte PathPointTypeStart = 0; // move + const byte PathPointTypeLine = 1; // line + const byte PathPointTypeBezier = 3; // default Bezier (= cubic Bezier) + const byte PathPointTypePathTypeMask = 0x07; // type mask (lowest 3 bits). + const byte PathPointTypeCloseSubpath = 0x80; // closed flag + + public CoreGraphicsPath() + { } + + public CoreGraphicsPath(CoreGraphicsPath path) + { + _points = new List(path._points); + _types = new List(path._types); + } + + public void MoveOrLineTo(double x, double y) + { + // Make a MoveTo if there is no previous subpath or the previous subpath was closed. + // Otherwise make a LineTo. + if (_types.Count == 0 || (_types[_types.Count - 1] & PathPointTypeCloseSubpath) == PathPointTypeCloseSubpath) + MoveTo(x, y); + else + LineTo(x, y, false); + } + + public void MoveTo(double x, double y) + { + _points.Add(new XPoint(x, y)); + _types.Add(PathPointTypeStart); + } + + public void LineTo(double x, double y, bool closeSubpath) + { + if (_points.Count > 0 && _points[_points.Count - 1].Equals(new XPoint(x, y))) + return; + + _points.Add(new XPoint(x, y)); + _types.Add((byte)(PathPointTypeLine | (closeSubpath ? PathPointTypeCloseSubpath : 0))); + } + + public void BezierTo(double x1, double y1, double x2, double y2, double x3, double y3, bool closeSubpath) + { + _points.Add(new XPoint(x1, y1)); + _types.Add(PathPointTypeBezier); + _points.Add(new XPoint(x2, y2)); + _types.Add(PathPointTypeBezier); + _points.Add(new XPoint(x3, y3)); + _types.Add((byte)(PathPointTypeBezier | (closeSubpath ? PathPointTypeCloseSubpath : 0))); + } + + /// + /// Adds an arc that fills exactly one quadrant (quarter) of an ellipse. + /// Just a quick hack to draw rounded rectangles before AddArc is fully implemented. + /// + public void QuadrantArcTo(double x, double y, double width, double height, int quadrant, bool clockwise) + { + if (width < 0) + throw new ArgumentOutOfRangeException("width"); + if (height < 0) + throw new ArgumentOutOfRangeException("height"); + + double w = Const.κ * width; + double h = Const.κ * height; + double x1, y1, x2, y2, x3, y3; + switch (quadrant) + { + case 1: + if (clockwise) + { + x1 = x + w; + y1 = y - height; + x2 = x + width; + y2 = y - h; + x3 = x + width; + y3 = y; + } + else + { + x1 = x + width; + y1 = y - h; + x2 = x + w; + y2 = y - height; + x3 = x; + y3 = y - height; + } + break; + + case 2: + if (clockwise) + { + x1 = x - width; + y1 = y - h; + x2 = x - w; + y2 = y - height; + x3 = x; + y3 = y - height; + } + else + { + x1 = x - w; + y1 = y - height; + x2 = x - width; + y2 = y - h; + x3 = x - width; + y3 = y; + } + break; + + case 3: + if (clockwise) + { + x1 = x - w; + y1 = y + height; + x2 = x - width; + y2 = y + h; + x3 = x - width; + y3 = y; + } + else + { + x1 = x - width; + y1 = y + h; + x2 = x - w; + y2 = y + height; + x3 = x; + y3 = y + height; + } + break; + + case 4: + if (clockwise) + { + x1 = x + width; + y1 = y + h; + x2 = x + w; + y2 = y + height; + x3 = x; + y3 = y + height; + } + else + { + x1 = x + w; + y1 = y + height; + x2 = x + width; + y2 = y + h; + x3 = x + width; + y3 = y; + } + break; + + default: + throw new ArgumentOutOfRangeException("quadrant"); + } + BezierTo(x1, y1, x2, y2, x3, y3, false); + } + + /// + /// Closes the current subpath. + /// + public void CloseSubpath() + { + int count = _types.Count; + if (count > 0) + _types[count - 1] |= PathPointTypeCloseSubpath; + } + + /// + /// Gets or sets the current fill mode (alternate or winding). + /// + XFillMode FillMode + { + get { return _fillMode; } + set { _fillMode = value; } + } + XFillMode _fillMode; + + public void AddArc(double x, double y, double width, double height, double startAngle, double sweepAngle) + { + XMatrix matrix = XMatrix.Identity; + List points = GeometryHelper.BezierCurveFromArc(x, y, width, height, startAngle, sweepAngle, PathStart.MoveTo1st, ref matrix); + int count = points.Count; + Debug.Assert((count + 2) % 3 == 0); + + MoveOrLineTo(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx += 3) + BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, points[idx + 2].X, points[idx + 2].Y, false); + } + + public void AddArc(XPoint point1, XPoint point2, XSize size, double rotationAngle, bool isLargeArg, XSweepDirection sweepDirection) + { + List points = GeometryHelper.BezierCurveFromArc(point1, point2, size, rotationAngle, isLargeArg, + sweepDirection == XSweepDirection.Clockwise, PathStart.MoveTo1st); + int count = points.Count; + Debug.Assert((count + 2) % 3 == 0); + + MoveOrLineTo(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx += 3) + BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, points[idx + 2].X, points[idx + 2].Y, false); + } + + public void AddCurve(XPoint[] points, double tension) + { + int count = points.Length; + if (count < 2) + throw new ArgumentException("AddCurve requires two or more points.", "points"); + + tension /= 3; + MoveOrLineTo(points[0].X, points[0].Y); + if (count == 2) + { + //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); + ToCurveSegment(points[0], points[0], points[1], points[1], tension); + } + else + { + //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension)); + ToCurveSegment(points[0], points[0], points[1], points[2], tension); + for (int idx = 1; idx < count - 2; idx++) + { + //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); + ToCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension); + } + //figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension)); + ToCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension); + } + } + + ///// + ///// Appends a Bézier curve for a cardinal spline through pt1 and pt2. + ///// + //void ToCurveSegment(double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double tension3, bool closeSubpath) + //{ + // BezierTo( + // x1 + tension3 * (x2 - x0), y1 + tension3 * (y2 - y0), + // x2 - tension3 * (x3 - x1), y2 - tension3 * (y3 - y1), + // x2, y2, closeSubpath); + //} + + void ToCurveSegment(XPoint pt0, XPoint pt1, XPoint pt2, XPoint pt3, double tension3) + { + BezierTo( + pt1.X + tension3 * (pt2.X - pt0.X), pt1.Y + tension3 * (pt2.Y - pt0.Y), + pt2.X - tension3 * (pt3.X - pt1.X), pt2.Y - tension3 * (pt3.Y - pt1.Y), + pt2.X, pt2.Y, + false); + } + + /// + /// Gets the path points in GDI+ style. + /// + public XPoint[] PathPoints { get { return _points.ToArray(); } } + + /// + /// Gets the path types in GDI+ style. + /// + public byte[] PathTypes { get { return _types.ToArray(); } } + + readonly List _points = new List(); + readonly List _types = new List(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyCache.cs b/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyCache.cs new file mode 100644 index 00000000..2526d870 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyCache.cs @@ -0,0 +1,142 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; +#if CORE || GDI +using System.Drawing; +using GdiFontFamily = System.Drawing.FontFamily; +#endif +#if WPF +using System.Windows.Media; +using System.Windows.Markup; +using WpfFontFamily = System.Windows.Media.FontFamily; +#endif +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; +using PdfSharp.Pdf; + +namespace PdfSharp.Drawing +{ + /// + /// Global cache of all internal font family objects. + /// + internal sealed class FontFamilyCache + { + FontFamilyCache() + { + _familiesByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public static FontFamilyInternal GetFamilyByName(string familyName) + { + try + { + Lock.EnterFontFactory(); + FontFamilyInternal family; + Singleton._familiesByName.TryGetValue(familyName, out family); + return family; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Caches the font family or returns a previously cached one. + /// + public static FontFamilyInternal CacheOrGetFontFamily(FontFamilyInternal fontFamily) + { + try + { + Lock.EnterFontFactory(); + // Recall that a font family is uniquely identified by its case insensitive name. + FontFamilyInternal existingFontFamily; + if (Singleton._familiesByName.TryGetValue(fontFamily.Name, out existingFontFamily)) + { +#if DEBUG_ + if (fontFamily.Name == "xxx") + fontFamily.GetType(); +#endif + return existingFontFamily; + } + Singleton._familiesByName.Add(fontFamily.Name, fontFamily); + return fontFamily; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Gets the singleton. + /// + static FontFamilyCache Singleton + { + get + { + // ReSharper disable once InvertIf + if (_singleton == null) + { + try + { + Lock.EnterFontFactory(); + if (_singleton == null) + _singleton = new FontFamilyCache(); + } + finally { Lock.ExitFontFactory(); } + } + return _singleton; + } + } + static volatile FontFamilyCache _singleton; + + internal static string GetCacheState() + { + StringBuilder state = new StringBuilder(); + state.Append("====================\n"); + state.Append("Font families by name\n"); + Dictionary.KeyCollection familyKeys = Singleton._familiesByName.Keys; + int count = familyKeys.Count; + string[] keys = new string[count]; + familyKeys.CopyTo(keys, 0); + Array.Sort(keys, StringComparer.OrdinalIgnoreCase); + foreach (string key in keys) + state.AppendFormat(" {0}: {1}\n", key, Singleton._familiesByName[key].DebuggerDisplay); + state.Append("\n"); + return state.ToString(); + } + + /// + /// Maps family name to internal font family. + /// + readonly Dictionary _familiesByName; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyInternal.cs b/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyInternal.cs new file mode 100644 index 00000000..19407718 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/FontFamilyInternal.cs @@ -0,0 +1,208 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Internal; +#if CORE || GDI +using System.Drawing; +using GdiFontFamily = System.Drawing.FontFamily; +#endif +#if WPF +using System.Windows.Media; +using System.Windows.Markup; +using WpfFontFamily = System.Windows.Media.FontFamily; +#endif + +// ReSharper disable ConvertToAutoProperty +// ReSharper disable ConvertPropertyToExpressionBody + +namespace PdfSharp.Drawing +{ + /// + /// Internal implementation class of XFontFamily. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal class FontFamilyInternal + { + // Implementation Notes + // FontFamilyInternal implements an XFontFamily. + // + // * Each XFontFamily object is just a handle to its FontFamilyInternal singleton. + // + // * A FontFamilyInternal is uniquely identified by its name. It + // is not possible to use two different fonts that have the same + // family name. + + FontFamilyInternal(string familyName, bool createPlatformObjects) + { + _sourceName = _name = familyName; +#if CORE || GDI + if (createPlatformObjects) + { + _gdiFontFamily = new GdiFontFamily(familyName); + _name = _gdiFontFamily.Name; + } +#endif +#if WPF && !SILVERLIGHT + if (createPlatformObjects) + { + _wpfFontFamily = new WpfFontFamily(familyName); + _name = _wpfFontFamily.FamilyNames[FontHelper.XmlLanguageEnUs]; + } +#endif +#if SILVERLIGHT + _wpfFontFamily = new WpfFontFamily(_name); + _name = _wpfFontFamily.Source; // Not expected to change _name. +#endif + } + +#if CORE || GDI + FontFamilyInternal(GdiFontFamily gdiFontFamily) + { + _sourceName = _name = gdiFontFamily.Name; + _gdiFontFamily = gdiFontFamily; +#if WPF + // Hybrid build only. + _wpfFontFamily = new WpfFontFamily(gdiFontFamily.Name); +#endif + } +#endif + +#if WPF + FontFamilyInternal(WpfFontFamily wpfFontFamily) + { +#if !SILVERLIGHT + _sourceName = wpfFontFamily.Source; + _name = wpfFontFamily.FamilyNames[FontHelper.XmlLanguageEnUs]; + _wpfFontFamily = wpfFontFamily; +#else + _sourceName = _name = wpfFontFamily.Source; + _wpfFontFamily = wpfFontFamily; +#endif +#if GDI + // Hybrid build only. + _gdiFontFamily = new GdiFontFamily(_sourceName); +#endif + } +#endif + + internal static FontFamilyInternal GetOrCreateFromName(string familyName, bool createPlatformObject) + { + try + { + Lock.EnterFontFactory(); + FontFamilyInternal family = FontFamilyCache.GetFamilyByName(familyName); + if (family == null) + { + family = new FontFamilyInternal(familyName, createPlatformObject); + family = FontFamilyCache.CacheOrGetFontFamily(family); + } + return family; + } + finally { Lock.ExitFontFactory(); } + } + +#if CORE || GDI + internal static FontFamilyInternal GetOrCreateFromGdi(GdiFontFamily gdiFontFamily) + { + try + { + Lock.EnterFontFactory(); + FontFamilyInternal fontFamily = new FontFamilyInternal(gdiFontFamily); + fontFamily = FontFamilyCache.CacheOrGetFontFamily(fontFamily); + return fontFamily; + } + finally { Lock.ExitFontFactory(); } + } +#endif + +#if WPF + internal static FontFamilyInternal GetOrCreateFromWpf(WpfFontFamily wpfFontFamily) + { + FontFamilyInternal fontFamily = new FontFamilyInternal(wpfFontFamily); + fontFamily = FontFamilyCache.CacheOrGetFontFamily(fontFamily); + return fontFamily; + } +#endif + + /// + /// Gets the family name this family was originally created with. + /// + public string SourceName + { + get { return _sourceName; } + } + readonly string _sourceName; + + /// + /// Gets the name that uniquely identifies this font family. + /// + public string Name + { + // In WPF this is the Win32FamilyName, not the WPF family name. + get { return _name; } + } + readonly string _name; + +#if CORE || GDI + /// + /// Gets the underlying GDI+ font family object. + /// Is null if the font was created by a font resolver. + /// + public GdiFontFamily GdiFamily + { + get { return _gdiFontFamily; } + } + readonly GdiFontFamily _gdiFontFamily; +#endif + +#if WPF + /// + /// Gets the underlying WPF font family object. + /// Is null if the font was created by a font resolver. + /// + public WpfFontFamily WpfFamily + { + get { return _wpfFontFamily; } + } + readonly WpfFontFamily _wpfFontFamily; +#endif + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSha rper disable UnusedMember.Local + internal string DebuggerDisplay + // ReShar per restore UnusedMember.Local + { + get { return string.Format(CultureInfo.InvariantCulture, "FontFamily: '{0}'", Name); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/FontHelper.cs b/src/PDFsharp/src/PdfSharp/Drawing/FontHelper.cs new file mode 100644 index 00000000..bfd35c92 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/FontHelper.cs @@ -0,0 +1,344 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +#if CORE || GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Markup; +using WpfFontStyle = System.Windows.FontStyle; +using WpfFontWeight = System.Windows.FontWeight; +using WpfBrush = System.Windows.Media.Brush; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif +#if NETFX_CORE +using Windows.UI.Text; +using Windows.UI.Xaml.Media; +#endif +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; + +namespace PdfSharp.Drawing +{ + /// + /// A bunch of functions that do not have a better place. + /// + static class FontHelper + { + /// + /// Measure string directly from font data. + /// + public static XSize MeasureString(string text, XFont font, XStringFormat stringFormat_notyetused) + { + XSize size = new XSize(); + + OpenTypeDescriptor descriptor = FontDescriptorCache.GetOrCreateDescriptorFor(font) as OpenTypeDescriptor; + if (descriptor != null) + { + // Height is the sum of ascender and descender. + size.Height = (descriptor.Ascender + descriptor.Descender) * font.Size / font.UnitsPerEm; + Debug.Assert(descriptor.Ascender > 0); + + bool symbol = descriptor.FontFace.cmap.symbol; + int length = text.Length; + int width = 0; + for (int idx = 0; idx < length; idx++) + { + char ch = text[idx]; + // HACK: Unclear what to do here. + if (ch < 32) + continue; + + if (symbol) + { + // Remap ch for symbol fonts. + ch = (char)(ch | (descriptor.FontFace.os2.usFirstCharIndex & 0xFF00)); // @@@ refactor + // Used | instead of + because of: http://pdfsharp.codeplex.com/workitem/15954 + } + int glyphIndex = descriptor.CharCodeToGlyphIndex(ch); + width += descriptor.GlyphIndexToWidth(glyphIndex); + } + // What? size.Width = width * font.Size * (font.Italic ? 1 : 1) / descriptor.UnitsPerEm; + size.Width = width * font.Size / descriptor.UnitsPerEm; + + // Adjust bold simulation. + if ((font.GlyphTypeface.StyleSimulations & XStyleSimulations.BoldSimulation) == XStyleSimulations.BoldSimulation) + { + // Add 2% of the em-size for each character. + // Unsure how to deal with white space. Currently count as regular character. + size.Width += length * font.Size * Const.BoldEmphasis; + } + } + Debug.Assert(descriptor != null, "No OpenTypeDescriptor."); + return size; + } + +#if CORE || GDI + public static GdiFont CreateFont(string familyName, double emSize, GdiFontStyle style, out XFontSource fontSource) + { + fontSource = null; + // ReSharper disable once JoinDeclarationAndInitializer + GdiFont font; + + // Use font resolver in CORE build. XPrivateFontCollection exists only in GDI and WPF build. +#if GDI + // Try private font collection first. + font = XPrivateFontCollection.TryCreateFont(familyName, emSize, style, out fontSource); + if (font != null) + { + // Get font source is different for this font because Win32 does not know it. + return font; + } +#endif + // Create ordinary Win32 font. + font = new GdiFont(familyName, (float)emSize, style, GraphicsUnit.World); + return font; + } +#endif + +#if WPF +#if !SILVERLIGHT + public static readonly CultureInfo CultureInfoEnUs = CultureInfo.GetCultureInfo("en-US"); + public static readonly XmlLanguage XmlLanguageEnUs = XmlLanguage.GetLanguage("en-US"); +#endif + /// + /// Creates a typeface. + /// + public static Typeface CreateTypeface(WpfFontFamily family, XFontStyle style) + { + // BUG: does not work with fonts that have others than the four default styles + WpfFontStyle fontStyle = FontStyleFromStyle(style); + WpfFontWeight fontWeight = FontWeightFromStyle(style); +#if !SILVERLIGHT + WpfTypeface typeface = new WpfTypeface(family, fontStyle, fontWeight, FontStretches.Normal); +#else + WpfTypeface typeface = null; +#endif + return typeface; + } + +#if !SILVERLIGHT + /// + /// Creates the formatted text. + /// + public static FormattedText CreateFormattedText(string text, Typeface typeface, double emSize, WpfBrush brush) + { + //FontFamily fontFamily = new FontFamily(testFontName); + //typeface = new Typeface(fontFamily, FontStyles.Normal, FontWeights.Bold, FontStretches.Condensed); + //List typefaces = new List(fontFamily.GetTypefaces()); + //typefaces.GetType(); + //typeface = s_typefaces[0]; + + // BUG: does not work with fonts that have others than the four default styles + FormattedText formattedText = new FormattedText(text, new CultureInfo("en-us"), FlowDirection.LeftToRight, typeface, emSize, brush); + // .NET 4.0 feature new NumberSubstitution(), TextFormattingMode.Display); + //formattedText.SetFontWeight(FontWeights.Bold); + //formattedText.SetFontStyle(FontStyles.Oblique); + //formattedText.SetFontStretch(FontStretches.Condensed); + return formattedText; + } +#endif + +#if SILVERLIGHT_ + /// + /// Creates the TextBlock. + /// + public static TextBlock CreateTextBlock(string text, XGlyphTypeface glyphTypeface, double emSize, Brush brush) + { + TextBlock textBlock = new TextBlock(); + textBlock.FontFamily = glyphTypeface.FontFamily; + textBlock.FontSource = glyphTypeface.FontSource; + textBlock.FontSize = emSize; + textBlock.FontWeight = glyphTypeface.IsBold ? FontWeights.Bold : FontWeights.Normal; + textBlock.FontStyle = glyphTypeface.IsItalic ? FontStyles.Italic : FontStyles.Normal; + textBlock.Foreground = brush; + textBlock.Text = text; + + return textBlock; + } +#endif + + /// + /// Simple hack to make it work... + /// Returns Normal or Italic - bold, underline and such get lost here. + /// + public static WpfFontStyle FontStyleFromStyle(XFontStyle style) + { + switch (style & XFontStyle.BoldItalic) // Mask out Underline, Strikeout, etc. + { + case XFontStyle.Regular: + return FontStyles.Normal; + + case XFontStyle.Bold: + return FontStyles.Normal; + + case XFontStyle.Italic: + return FontStyles.Italic; + + case XFontStyle.BoldItalic: + return FontStyles.Italic; + } + return FontStyles.Normal; + } + + /// + /// Simple hack to make it work... + /// + public static FontWeight FontWeightFromStyle(XFontStyle style) + { + switch (style & XFontStyle.BoldItalic) // Mask out Underline, Strikeout, etc. + { + case XFontStyle.Regular: + return FontWeights.Normal; + + case XFontStyle.Bold: + return FontWeights.Bold; + + case XFontStyle.Italic: + return FontWeights.Normal; + + case XFontStyle.BoldItalic: + return FontWeights.Bold; + } + return FontWeights.Normal; + } + + /// + /// Determines whether the style is available as a glyph type face in the specified font family, i.e. the specified style is not simulated. + /// + public static bool IsStyleAvailable(XFontFamily family, XGdiFontStyle style) + { + style &= XGdiFontStyle.BoldItalic; +#if !SILVERLIGHT + // TODOWPF: check for correctness + // FontDescriptor descriptor = FontDescriptorCache.GetOrCreateDescriptor(family.Name, style); + //XFontMetrics metrics = descriptor.FontMetrics; + + // style &= XFontStyle.Regular | XFontStyle.Bold | XFontStyle.Italic | XFontStyle.BoldItalic; // same as XFontStyle.BoldItalic + List typefaces = new List(family.WpfFamily.GetTypefaces()); + foreach (WpfTypeface typeface in typefaces) + { + bool bold = typeface.Weight == FontWeights.Bold; + bool italic = typeface.Style == FontStyles.Italic; + switch (style) + { + case XGdiFontStyle.Regular: + if (!bold && !italic) + return true; + break; + + case XGdiFontStyle.Bold: + if (bold && !italic) + return true; + break; + + case XGdiFontStyle.Italic: + if (!bold && italic) + return true; + break; + + case XGdiFontStyle.BoldItalic: + if (bold && italic) + return true; + break; + } + ////// typeface.sty + ////// bool available = false; + ////// GlyphTypeface glyphTypeface; + ////// if (typeface.TryGetGlyphTypeface(out glyphTypeface)) + ////// { + //////#if DEBUG_ + ////// glyphTypeface.GetType(); + //////#endif + ////// available = true; + ////// } + ////// if (available) + ////// return true; + } + return false; +#else + return true; // AGHACK +#endif + } +#endif + + /// + /// Calculates an Adler32 checksum combined with the buffer length + /// in a 64 bit unsigned integer. + /// + public static ulong CalcChecksum(byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + const uint prime = 65521; // largest prime smaller than 65536 + uint s1 = 0; + uint s2 = 0; + int length = buffer.Length; + int offset = 0; + while (length > 0) + { + int n = 3800; + if (n > length) + n = length; + length -= n; + while (--n >= 0) + { + s1 += buffer[offset++]; + s2 = s2 + s1; + } + s1 %= prime; + s2 %= prime; + } + //return ((ulong)((ulong)(((ulong)s2 << 16) | (ulong)s1)) << 32) | (ulong)buffer.Length; + ulong ul1 = (ulong)s2 << 16; + ul1 = ul1 | s1; + ulong ul2 = (ulong)buffer.Length; + return (ul1 << 32) | ul2; + } + + public static XFontStyle CreateStyle(bool isBold, bool isItalic) + { + return (isBold ? XFontStyle.Bold : 0) | (isItalic ? XFontStyle.Italic : 0); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/GeometryHelper.cs b/src/PDFsharp/src/PdfSharp/Drawing/GeometryHelper.cs new file mode 100644 index 00000000..ce1cfa16 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/GeometryHelper.cs @@ -0,0 +1,874 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +#endif +#if NETFX_CORE || UWP +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +#endif +using PdfSharp.Internal; + +// ReSharper disable RedundantNameQualifier +// ReSharper disable CompareOfFloatsByEqualityOperator + +namespace PdfSharp.Drawing +{ + /// + /// Helper class for Geometry paths. + /// + static class GeometryHelper + { +#if WPF || NETFX_CORE + /// + /// Appends a Bézier segment from a curve. + /// + public static BezierSegment CreateCurveSegment(XPoint pt0, XPoint pt1, XPoint pt2, XPoint pt3, double tension3) + { +#if !SILVERLIGHT && !NETFX_CORE + return new BezierSegment( + new SysPoint(pt1.X + tension3 * (pt2.X - pt0.X), pt1.Y + tension3 * (pt2.Y - pt0.Y)), + new SysPoint(pt2.X - tension3 * (pt3.X - pt1.X), pt2.Y - tension3 * (pt3.Y - pt1.Y)), + new SysPoint(pt2.X, pt2.Y), true); +#else + BezierSegment bezierSegment = new BezierSegment(); + bezierSegment.Point1 = new SysPoint(pt1.X + tension3 * (pt2.X - pt0.X), pt1.Y + tension3 * (pt2.Y - pt0.Y)); + bezierSegment.Point2 = new SysPoint(pt2.X - tension3 * (pt3.X - pt1.X), pt2.Y - tension3 * (pt3.Y - pt1.Y)); + bezierSegment.Point3 = new SysPoint(pt2.X, pt2.Y); + return bezierSegment; +#endif + } +#endif + +#if WPF || NETFX_CORE + /// + /// Creates a path geometry from a polygon. + /// + public static PathGeometry CreatePolygonGeometry(SysPoint[] points, XFillMode fillMode, bool closed) + { + PolyLineSegment seg = new PolyLineSegment(); + int count = points.Length; + // For correct drawing the start point of the segment must not be the same as the first point. + for (int idx = 1; idx < count; idx++) + seg.Points.Add(new SysPoint(points[idx].X, points[idx].Y)); +#if !SILVERLIGHT && !NETFX_CORE + seg.IsStroked = true; +#endif + PathFigure fig = new PathFigure(); + fig.StartPoint = new SysPoint(points[0].X, points[0].Y); + fig.Segments.Add(seg); + fig.IsClosed = closed; + PathGeometry geo = new PathGeometry(); + geo.FillRule = fillMode == XFillMode.Winding ? FillRule.Nonzero : FillRule.EvenOdd; + geo.Figures.Add(fig); + return geo; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Creates a path geometry from a polygon. + /// + public static PolyLineSegment CreatePolyLineSegment(SysPoint[] points, XFillMode fillMode, bool closed) + { + PolyLineSegment seg = new PolyLineSegment(); + int count = points.Length; + // For correct drawing the start point of the segment must not be the same as the first point. + for (int idx = 1; idx < count; idx++) + seg.Points.Add(new SysPoint(points[idx].X, points[idx].Y)); +#if !SILVERLIGHT && !NETFX_CORE + seg.IsStroked = true; +#endif + return seg; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Creates the arc segment from parameters of the GDI+ DrawArc function. + /// + public static ArcSegment CreateArcSegment(double x, double y, double width, double height, double startAngle, + double sweepAngle, out SysPoint startPoint) + { + // Normalize the angles. + double α = startAngle; + if (α < 0) + α = α + (1 + Math.Floor((Math.Abs(α) / 360))) * 360; + else if (α > 360) + α = α - Math.Floor(α / 360) * 360; + Debug.Assert(α >= 0 && α <= 360); + + if (Math.Abs(sweepAngle) >= 360) + sweepAngle = Math.Sign(sweepAngle) * 360; + double β = startAngle + sweepAngle; + if (β < 0) + β = β + (1 + Math.Floor((Math.Abs(β) / 360))) * 360; + else if (β > 360) + β = β - Math.Floor(β / 360) * 360; + + if (α == 0 && β < 0) + α = 360; + else if (α == 360 && β > 0) + α = 0; + + // Scanling factor. + double δx = width / 2; + double δy = height / 2; + + // Center of ellipse. + double x0 = x + δx; + double y0 = y + δy; + + double cosα, cosβ, sinα, sinβ; + if (width == height) + { + // Circular arc needs no correction. + α = α * Calc.Deg2Rad; + β = β * Calc.Deg2Rad; + } + else + { + // Elliptic arc needs the angles to be adjusted such that the scaling transformation is compensated. + α = α * Calc.Deg2Rad; + sinα = Math.Sin(α); + if (Math.Abs(sinα) > 1E-10) + { + if (α < Math.PI) + α = Math.PI / 2 - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); + else + α = 3 * Math.PI / 2 - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); + } + //α = Calc.πHalf - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); + β = β * Calc.Deg2Rad; + sinβ = Math.Sin(β); + if (Math.Abs(sinβ) > 1E-10) + { + if (β < Math.PI) + β = Math.PI / 2 - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); + else + β = 3 * Math.PI / 2 - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); + } + //β = Calc.πHalf - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); + } + + sinα = Math.Sin(α); + cosα = Math.Cos(α); + sinβ = Math.Sin(β); + cosβ = Math.Cos(β); + + startPoint = new SysPoint(x0 + δx * cosα, y0 + δy * sinα); + SysPoint destPoint = new SysPoint(x0 + δx * cosβ, y0 + δy * sinβ); + SysSize size = new SysSize(δx, δy); + bool isLargeArc = Math.Abs(sweepAngle) >= 180; + SweepDirection sweepDirection = sweepAngle > 0 ? SweepDirection.Clockwise : SweepDirection.Counterclockwise; +#if !SILVERLIGHT && !NETFX_CORE + bool isStroked = true; + ArcSegment seg = new ArcSegment(destPoint, size, 0, isLargeArc, sweepDirection, isStroked); +#else + ArcSegment seg = new ArcSegment(); + seg.Point = destPoint; + seg.Size = size; + seg.RotationAngle = 0; + seg.IsLargeArc = isLargeArc; + seg.SweepDirection = sweepDirection; + // isStroked does not exist in Silverlight 3 +#endif + return seg; + } +#endif + + /// + /// Creates between 1 and 5 Béziers curves from parameters specified like in GDI+. + /// + public static List BezierCurveFromArc(double x, double y, double width, double height, double startAngle, double sweepAngle, + PathStart pathStart, ref XMatrix matrix) + { + List points = new List(); + + // Normalize the angles. + double α = startAngle; + if (α < 0) + α = α + (1 + Math.Floor((Math.Abs(α) / 360))) * 360; + else if (α > 360) + α = α - Math.Floor(α / 360) * 360; + Debug.Assert(α >= 0 && α <= 360); + + double β = sweepAngle; + if (β < -360) + β = -360; + else if (β > 360) + β = 360; + + if (α == 0 && β < 0) + α = 360; + else if (α == 360 && β > 0) + α = 0; + + // Is it possible that the arc is small starts and ends in same quadrant? + bool smallAngle = Math.Abs(β) <= 90; + + β = α + β; + if (β < 0) + β = β + (1 + Math.Floor((Math.Abs(β) / 360))) * 360; + + bool clockwise = sweepAngle > 0; + int startQuadrant = Quadrant(α, true, clockwise); + int endQuadrant = Quadrant(β, false, clockwise); + + if (startQuadrant == endQuadrant && smallAngle) + AppendPartialArcQuadrant(points, x, y, width, height, α, β, pathStart, matrix); + else + { + int currentQuadrant = startQuadrant; + bool firstLoop = true; + do + { + if (currentQuadrant == startQuadrant && firstLoop) + { + double ξ = currentQuadrant * 90 + (clockwise ? 90 : 0); + AppendPartialArcQuadrant(points, x, y, width, height, α, ξ, pathStart, matrix); + } + else if (currentQuadrant == endQuadrant) + { + double ξ = currentQuadrant * 90 + (clockwise ? 0 : 90); + AppendPartialArcQuadrant(points, x, y, width, height, ξ, β, PathStart.Ignore1st, matrix); + } + else + { + double ξ1 = currentQuadrant * 90 + (clockwise ? 0 : 90); + double ξ2 = currentQuadrant * 90 + (clockwise ? 90 : 0); + AppendPartialArcQuadrant(points, x, y, width, height, ξ1, ξ2, PathStart.Ignore1st, matrix); + } + + // Don't stop immediately if arc is greater than 270 degrees. + if (currentQuadrant == endQuadrant && smallAngle) + break; + smallAngle = true; + + if (clockwise) + currentQuadrant = currentQuadrant == 3 ? 0 : currentQuadrant + 1; + else + currentQuadrant = currentQuadrant == 0 ? 3 : currentQuadrant - 1; + + firstLoop = false; + } while (true); + } + return points; + } + + /// + /// Calculates the quadrant (0 through 3) of the specified angle. If the angle lies on an edge + /// (0, 90, 180, etc.) the result depends on the details how the angle is used. + /// + static int Quadrant(double φ, bool start, bool clockwise) + { + Debug.Assert(φ >= 0); + if (φ > 360) + φ = φ - Math.Floor(φ / 360) * 360; + + int quadrant = (int)(φ / 90); + if (quadrant * 90 == φ) + { + if ((start && !clockwise) || (!start && clockwise)) + quadrant = quadrant == 0 ? 3 : quadrant - 1; + } + else + quadrant = clockwise ? ((int)Math.Floor(φ / 90)) % 4 : (int)Math.Floor(φ / 90); + return quadrant; + } + + /// + /// Appends a Bézier curve for an arc within a full quadrant. + /// + static void AppendPartialArcQuadrant(List points, double x, double y, double width, double height, double α, double β, PathStart pathStart, XMatrix matrix) + { + Debug.Assert(α >= 0 && α <= 360); + Debug.Assert(β >= 0); + if (β > 360) + β = β - Math.Floor(β / 360) * 360; + Debug.Assert(Math.Abs(α - β) <= 90); + + // Scanling factor. + double δx = width / 2; + double δy = height / 2; + + // Center of ellipse. + double x0 = x + δx; + double y0 = y + δy; + + // We have the following quarters: + // | + // 2 | 3 + // ----+----- + // 1 | 0 + // | + // If the angles lie in quarter 2 or 3, their values are subtracted by 180 and the + // resulting curve is reflected at the center. This algorithm works as expected (simply tried out). + // There may be a mathematically more elegant solution... + bool reflect = false; + if (α >= 180 && β >= 180) + { + α -= 180; + β -= 180; + reflect = true; + } + + double cosα, cosβ, sinα, sinβ; + if (width == height) + { + // Circular arc needs no correction. + α = α * Calc.Deg2Rad; + β = β * Calc.Deg2Rad; + } + else + { + // Elliptic arc needs the angles to be adjusted such that the scaling transformation is compensated. + α = α * Calc.Deg2Rad; + sinα = Math.Sin(α); + if (Math.Abs(sinα) > 1E-10) + α = Math.PI / 2 - Math.Atan(δy * Math.Cos(α) / (δx * sinα)); + β = β * Calc.Deg2Rad; + sinβ = Math.Sin(β); + if (Math.Abs(sinβ) > 1E-10) + β = Math.PI / 2 - Math.Atan(δy * Math.Cos(β) / (δx * sinβ)); + } + + double κ = 4 * (1 - Math.Cos((α - β) / 2)) / (3 * Math.Sin((β - α) / 2)); + sinα = Math.Sin(α); + cosα = Math.Cos(α); + sinβ = Math.Sin(β); + cosβ = Math.Cos(β); + + //XPoint pt1, pt2, pt3; + if (!reflect) + { + // Calculation for quarter 0 and 1. + switch (pathStart) + { + case PathStart.MoveTo1st: + points.Add(matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα))); + break; + + case PathStart.LineTo1st: + points.Add(matrix.Transform(new XPoint(x0 + δx * cosα, y0 + δy * sinα))); + break; + + case PathStart.Ignore1st: + break; + } + points.Add(matrix.Transform(new XPoint(x0 + δx * (cosα - κ * sinα), y0 + δy * (sinα + κ * cosα)))); + points.Add(matrix.Transform(new XPoint(x0 + δx * (cosβ + κ * sinβ), y0 + δy * (sinβ - κ * cosβ)))); + points.Add(matrix.Transform(new XPoint(x0 + δx * cosβ, y0 + δy * sinβ))); + } + else + { + // Calculation for quarter 2 and 3. + switch (pathStart) + { + case PathStart.MoveTo1st: + points.Add(matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα))); + break; + + case PathStart.LineTo1st: + points.Add(matrix.Transform(new XPoint(x0 - δx * cosα, y0 - δy * sinα))); + break; + + case PathStart.Ignore1st: + break; + } + points.Add(matrix.Transform(new XPoint(x0 - δx * (cosα - κ * sinα), y0 - δy * (sinα + κ * cosα)))); + points.Add(matrix.Transform(new XPoint(x0 - δx * (cosβ + κ * sinβ), y0 - δy * (sinβ - κ * cosβ)))); + points.Add(matrix.Transform(new XPoint(x0 - δx * cosβ, y0 - δy * sinβ))); + } + } + + /// + /// Creates between 1 and 5 Béziers curves from parameters specified like in WPF. + /// + public static List BezierCurveFromArc(XPoint point1, XPoint point2, XSize size, + double rotationAngle, bool isLargeArc, bool clockwise, PathStart pathStart) + { + // See also http://www.charlespetzold.com/blog/blog.xml from January 2, 2008: + // http://www.charlespetzold.com/blog/2008/01/Mathematics-of-ArcSegment.html + double δx = size.Width; + double δy = size.Height; + Debug.Assert(δx * δy > 0); + double factor = δy / δx; + bool isCounterclockwise = !clockwise; + + // Adjust for different radii and rotation angle. + XMatrix matrix = new XMatrix(); + matrix.RotateAppend(-rotationAngle); + matrix.ScaleAppend(δy / δx, 1); + XPoint pt1 = matrix.Transform(point1); + XPoint pt2 = matrix.Transform(point2); + + // Get info about chord that connects both points. + XPoint midPoint = new XPoint((pt1.X + pt2.X) / 2, (pt1.Y + pt2.Y) / 2); + XVector vect = pt2 - pt1; + double halfChord = vect.Length / 2; + + // Get vector from chord to center. + XVector vectRotated; + + // (comparing two Booleans here!) + if (isLargeArc == isCounterclockwise) + vectRotated = new XVector(-vect.Y, vect.X); + else + vectRotated = new XVector(vect.Y, -vect.X); + + vectRotated.Normalize(); + + // Distance from chord to center. + double centerDistance = Math.Sqrt(δy * δy - halfChord * halfChord); + if (double.IsNaN(centerDistance)) + centerDistance = 0; + + // Calculate center point. + XPoint center = midPoint + centerDistance * vectRotated; + + // Get angles from center to the two points. + double α = Math.Atan2(pt1.Y - center.Y, pt1.X - center.X); + double β = Math.Atan2(pt2.Y - center.Y, pt2.X - center.X); + + // (another comparison of two Booleans!) + if (isLargeArc == (Math.Abs(β - α) < Math.PI)) + { + if (α < β) + α += 2 * Math.PI; + else + β += 2 * Math.PI; + } + + // Invert matrix for final point calculation. + matrix.Invert(); + double sweepAngle = β - α; + + // Let the algorithm of GDI+ DrawArc to Bézier curves do the rest of the job + return BezierCurveFromArc(center.X - δx * factor, center.Y - δy, 2 * δx * factor, 2 * δy, + α / Calc.Deg2Rad, sweepAngle / Calc.Deg2Rad, pathStart, ref matrix); + } + + + + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // The code below comes from WPF source code, because I was not able to convert an arc + // to a series of Bezier curves exactly the way WPF renders the arc. I tested my own code + // with the MinBar Test Suite from QualityLogic and could not find out why it does not match. + // My Bezier curves came very close to the arc, but in some cases they do simply not match. + // So I gave up and use the WPF code. +#if WPF || NETFX_CORE + + // ReSharper disable InconsistentNaming + const double FUZZ = 1e-6; // Relative 0 + // ReSharper restore InconsistentNaming + + //+------------------------------------------------------------------------------------------------- + + // + // Function: GetArcAngle + // + // Synopsis: Get the number of Bezier arcs, and sine & cosine of each + // + // Notes: This is a private utility used by ArcToBezier + // We break the arc into pieces so that no piece will span more than 90 degrees. + // The input points are on the unit circle + // + //------------------------------------------------------------------------------------------------- + public static void + GetArcAngle( + XPoint startPoint, // Start point + XPoint endPoint, // End point + bool isLargeArc, // Choose the larger of the 2 possible arcs if TRUE + //SweepDirection sweepDirection, // Direction n which to sweep the arc. + bool isClockwise, + out double cosArcAngle, // Cosine of a the sweep angle of one arc piece + out double sinArcAngle, // Sine of a the sweep angle of one arc piece + out int pieces) // Out: The number of pieces + { + double angle; + + // The points are on the unit circle, so: + cosArcAngle = startPoint.X * endPoint.X + startPoint.Y * endPoint.Y; + sinArcAngle = startPoint.X * endPoint.Y - startPoint.Y * endPoint.X; + + if (cosArcAngle >= 0) + { + if (isLargeArc) + { + // The angle is between 270 and 360 degrees, so + pieces = 4; + } + else + { + // The angle is between 0 and 90 degrees, so + pieces = 1; + return; // We already have the cosine and sine of the angle + } + } + else + { + if (isLargeArc) + { + // The angle is between 180 and 270 degrees, so + pieces = 3; + } + else + { + // The angle is between 90 and 180 degrees, so + pieces = 2; + } + } + + // We have to chop the arc into the computed number of pieces. For cPieces=2 and 4 we could + // have uses the half-angle trig formulas, but for pieces=3 it requires solving a cubic + // equation; the performance difference is not worth the extra code, so we'll get the angle, + // divide it, and get its sine and cosine. + + Debug.Assert(pieces > 0); + angle = Math.Atan2(sinArcAngle, cosArcAngle); + + if (isClockwise) + { + if (angle < 0) + angle += Math.PI * 2; + } + else + { + if (angle > 0) + angle -= Math.PI * 2; + } + + angle /= pieces; + cosArcAngle = Math.Cos(angle); + sinArcAngle = Math.Sin(angle); + } + + /******************************************************************************\ + * + * Function Description: + * + * Get the distance from a circular arc's endpoints to the control points of the + * Bezier arc that approximates it, as a fraction of the arc's radius. + * + * Since the result is relative to the arc's radius, it depends strictly on the + * arc's angle. The arc is assumed to be of 90 degrees of less, so the angle is + * determined by the cosine of that angle, which is derived from rDot = the dot + * product of two radius vectors. We need the Bezier curve that agrees with + * the arc's points and tangents at the ends and midpoint. Here we compute the + * distance from the curve's endpoints to its control points. + * + * Since we are looking for the relative distance, we can work on the unit + * circle. Place the center of the circle at the origin, and put the X axis as + * the bisector between the 2 vectors. Let a be the angle between the vectors. + * Then the X coordinates of the 1st & last points are cos(a/2). Let x be the X + * coordinate of the 2nd & 3rd points. At t=1/2 we have a point at (1,0). + * But the terms of the polynomial there are all equal: + * + * (1-t)^3 = t*(1-t)^2 = 2^2*(1-t) = t^3 = 1/8, + * + * so from the Bezier formula there we have: + * + * 1 = (1/8) * (cos(a/2) + 3x + 3x + cos(a/2)), + * hence + * x = (1 - cos(a/2)) / 3 + * + * The X difference between that and the 1st point is: + * + * DX = x - cos(a/2) = 4(1 - cos(a/2)) / 3. + * + * But DX = distance / sin(a/2), hence the distance is + * + * dist = (4/3)*(1 - cos(a/2)) / sin(a/2). + * + * Created: 5/29/2001 [....] + * + /*****************************************************************************/ + public static double + GetBezierDistance( // Return the distance as a fraction of the radius + double dot, // In: The dot product of the two radius vectors + double radius) // In: The radius of the arc's circle (optional=1) + { + double radSquared = radius * radius; // Squared radius + + Debug.Assert(dot >= -radSquared * .1); // angle < 90 degrees + Debug.Assert(dot <= radSquared * 1.1); // as dot product of 2 radius vectors + + double dist = 0; // Acceptable fallback value + + /* Rather than the angle a, we are given rDot = R^2 * cos(a), so we + multiply top and bottom by R: + + dist = (4/3)*(R - Rcos(a/2)) / Rsin(a/2) + + and use some trig: + __________ + cos(a/2) = \/1 + cos(a) / 2 + ________________ __________ + R*cos(a/2) = \/R^2 + R^2 cos(a) / 2 = \/R^2 + rDot / 2 */ + + double cos = (radSquared + dot) / 2; // =(R*cos(a))^2 + if (cos < 0) + return dist; + // __________________ + // R*sin(a/2) = \/R^2 - R^2 cos(a/2) + + double sin = radSquared - cos; // =(R*sin(a))^2 + if (sin <= 0) + return dist; + + sin = Math.Sqrt(sin); // = R*cos(a) + cos = Math.Sqrt(cos); // = R*sin(a) + + dist = 4 * (radius - cos) / 3; + if (dist <= sin * FUZZ) + dist = 0; + else + dist = 4 * (radius - cos) / sin / 3; + + return dist; + } + + //+------------------------------------------------------------------------------------------------- + // + // Function: ArcToBezier + // + // Synopsis: Compute the Bezier approximation of an arc + // + // Notes: This utilitycomputes the Bezier approximation for an elliptical arc as it is defined + // in the SVG arc spec. The ellipse from which the arc is carved is axis-aligned in its + // own coordinates, and defined there by its x and y radii. The rotation angle defines + // how the ellipse's axes are rotated relative to our x axis. The start and end points + // define one of 4 possible arcs; the sweep and large-arc flags determine which one of + // these arcs will be chosen. See SVG spec for details. + // + // Returning pieces = 0 indicates a line instead of an arc + // pieces = -1 indicates that the arc degenerates to a point + // + //-------------------------------------------------------------------------------------------------- + public static PointCollection ArcToBezier(double xStart, double yStart, double xRadius, double yRadius, double rotationAngle, + bool isLargeArc, bool isClockwise, double xEnd, double yEnd, out int pieces) + { + double cosArcAngle, sinArcAngle, xCenter, yCenter, r, bezDist; + XVector vecToBez1, vecToBez2; + XMatrix matToEllipse; + + double fuzz2 = FUZZ * FUZZ; + bool isZeroCenter = false; + + pieces = -1; + + // In the following, the line segment between between the arc's start and + // end points is referred to as "the chord". + + // Transform 1: Shift the origin to the chord's midpoint + double x = (xEnd - xStart) / 2; + double y = (yEnd - yStart) / 2; + + double halfChord2 = x * x + y * y; // (half chord length)^2 + + // Degenerate case: single point + if (halfChord2 < fuzz2) + { + // The chord degeneartes to a point, the arc will be ignored + return null; + } + + // Degenerate case: straight line + if (!AcceptRadius(halfChord2, fuzz2, ref xRadius) || !AcceptRadius(halfChord2, fuzz2, ref yRadius)) + { + // We have a zero radius, add a straight line segment instead of an arc + pieces = 0; + return null; + } + + if (xRadius == 0 || yRadius == 0) + { + // We have a zero radius, add a straight line segment instead of an arc + pieces = 0; + return null; + } + + // Transform 2: Rotate to the ellipse's coordinate system + rotationAngle = -rotationAngle * Calc.Deg2Rad; + + double cos = Math.Cos(rotationAngle); + double sin = Math.Sin(rotationAngle); + + r = x * cos - y * sin; + y = x * sin + y * cos; + x = r; + + // Transform 3: Scale so that the ellipse will become a unit circle + x /= xRadius; + y /= yRadius; + + // We get to the center of that circle along a verctor perpendicular to the chord + // from the origin, which is the chord's midpoint. By Pythagoras, the length of that + // vector is sqrt(1 - (half chord)^2). + + halfChord2 = x * x + y * y; // now in the circle coordinates + + if (halfChord2 > 1) + { + // The chord is longer than the circle's diameter; we scale the radii uniformly so + // that the chord will be a diameter. The center will then be the chord's midpoint, + // which is now the origin. + r = Math.Sqrt(halfChord2); + xRadius *= r; + yRadius *= r; + xCenter = yCenter = 0; + isZeroCenter = true; + + // Adjust the unit-circle coordinates x and y + x /= r; + y /= r; + } + else + { + // The length of (-y,x) or (x,-y) is sqrt(rHalfChord2), and we want a vector + // of length sqrt(1 - rHalfChord2), so we'll multiply it by: + r = Math.Sqrt((1 - halfChord2) / halfChord2); + //if (isLargeArc != (eSweepDirection == SweepDirection.Clockwise)) + if (isLargeArc != isClockwise) + // Going to the center from the origin=chord-midpoint + { + // in the direction of (-y, x) + xCenter = -r * y; + yCenter = r * x; + } + else + { + // in the direction of (y, -x) + xCenter = r * y; + yCenter = -r * x; + } + } + + // Transformation 4: shift the origin to the center of the circle, which then becomes + // the unit circle. Since the chord's midpoint is the origin, the start point is (-x, -y) + // and the endpoint is (x, y). + XPoint ptStart = new XPoint(-x - xCenter, -y - yCenter); + XPoint ptEnd = new XPoint(x - xCenter, y - yCenter); + + // Set up the matrix that will take us back to our coordinate system. This matrix is + // the inverse of the combination of transformation 1 thru 4. + matToEllipse = new XMatrix(cos * xRadius, -sin * xRadius, + sin * yRadius, cos * yRadius, + (xEnd + xStart) / 2, (yEnd + yStart) / 2); + + if (!isZeroCenter) + { + // Prepend the translation that will take the origin to the circle's center + matToEllipse.OffsetX += (matToEllipse.M11 * xCenter + matToEllipse.M21 * yCenter); + matToEllipse.OffsetY += (matToEllipse.M12 * xCenter + matToEllipse.M22 * yCenter); + } + + // Get the sine & cosine of the angle that will generate the arc pieces + GetArcAngle(ptStart, ptEnd, isLargeArc, isClockwise, out cosArcAngle, out sinArcAngle, out pieces); + + // Get the vector to the first Bezier control point + bezDist = GetBezierDistance(cosArcAngle, 1); + + //if (eSweepDirection == SweepDirection.Counterclockwise) + if (!isClockwise) + bezDist = -bezDist; + + vecToBez1 = new XVector(-bezDist * ptStart.Y, bezDist * ptStart.X); + + PointCollection result = new PointCollection(); + + // Add the arc pieces, except for the last + for (int idx = 1; idx < pieces; idx++) + { + // Get the arc piece's endpoint + XPoint ptPieceEnd = new XPoint(ptStart.X * cosArcAngle - ptStart.Y * sinArcAngle, ptStart.X * sinArcAngle + ptStart.Y * cosArcAngle); + vecToBez2 = new XVector(-bezDist * ptPieceEnd.Y, bezDist * ptPieceEnd.X); + + result.Add(matToEllipse.Transform(ptStart + vecToBez1)); + result.Add(matToEllipse.Transform(ptPieceEnd - vecToBez2)); + result.Add(matToEllipse.Transform(ptPieceEnd)); + + // Move on to the next arc + ptStart = ptPieceEnd; + vecToBez1 = vecToBez2; + } + + // Last arc - we know the endpoint + vecToBez2 = new XVector(-bezDist * ptEnd.Y, bezDist * ptEnd.X); + + result.Add(matToEllipse.Transform(ptStart + vecToBez1)); + result.Add(matToEllipse.Transform(ptEnd - vecToBez2)); + result.Add(new XPoint(xEnd, yEnd)); + + return result; + } + + /// + /// Gets a value indicating whether radius large enough compared to the chord length. + /// + /// (1/2 chord length)squared + /// Squared fuzz. + /// The radius to accept (or not). + static bool AcceptRadius(double halfChord2, double fuzz2, ref double radius) + { + Debug.Assert(halfChord2 >= fuzz2); // Otherewise we have no guarantee that the radius is not 0, and we need to divide by the radius + bool accept = radius * radius > halfChord2 * fuzz2; + if (accept) + { + if (radius < 0) + radius = 0; + } + return accept; + } +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/GraphicsStateStack.cs b/src/PDFsharp/src/PdfSharp/Drawing/GraphicsStateStack.cs new file mode 100644 index 00000000..034c184f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/GraphicsStateStack.cs @@ -0,0 +1,98 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents a stack of XGraphicsState and XGraphicsContainer objects. + /// + internal class GraphicsStateStack + { + public GraphicsStateStack(XGraphics gfx) + { + _current = new InternalGraphicsState(gfx); + } + + public int Count + { + get { return _stack.Count; } + } + + public void Push(InternalGraphicsState state) + { + _stack.Push(state); + state.Pushed(); + } + + public int Restore(InternalGraphicsState state) + { + if (!_stack.Contains(state)) + throw new ArgumentException("State not on stack.", "state"); + if (state.Invalid) + throw new ArgumentException("State already restored.", "state"); + + int count = 1; + InternalGraphicsState top = _stack.Pop(); + top.Popped(); + while (top != state) + { + count++; + state.Invalid = true; + top = _stack.Pop(); + top.Popped(); + } + state.Invalid = true; + return count; + } + + public InternalGraphicsState Current + { + get + { + if (_stack.Count == 0) + return _current; + return _stack.Peek(); + } + } + + readonly InternalGraphicsState _current; + + readonly Stack _stack = new Stack(); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/IXGraphicsRenderer.cs b/src/PDFsharp/src/PdfSharp/Drawing/IXGraphicsRenderer.cs new file mode 100644 index 00000000..2b7a1305 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/IXGraphicsRenderer.cs @@ -0,0 +1,190 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents an abstract drawing surface for PdfPages. + /// + internal interface IXGraphicsRenderer + { + void Close(); + + #region Drawing + + ///// + ///// Fills the entire drawing surface with the specified color. + ///// + //[Obsolete("Will be removed.")] + //void Clear(XColor color); + + /// + /// Draws a straight line. + /// + void DrawLine(XPen pen, double x1, double y1, double x2, double y2); + + /// + /// Draws a series of straight lines. + /// + void DrawLines(XPen pen, XPoint[] points); + + /// + /// Draws a Bzier spline. + /// + void DrawBezier(XPen pen, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4); + + /// + /// Draws a series of Bzier splines. + /// + void DrawBeziers(XPen pen, XPoint[] points); + + /// + /// Draws a cardinal spline. + /// + void DrawCurve(XPen pen, XPoint[] points, double tension); + + /// + /// Draws an arc. + /// + void DrawArc(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle); + + /// + /// Draws a rectangle. + /// + void DrawRectangle(XPen pen, XBrush brush, double x, double y, double width, double height); + + /// + /// Draws a series of rectangles. + /// + void DrawRectangles(XPen pen, XBrush brush, XRect[] rects); + + /// + /// Draws a rectangle with rounded corners. + /// + void DrawRoundedRectangle(XPen pen, XBrush brush, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight); + + /// + /// Draws an ellipse. + /// + void DrawEllipse(XPen pen, XBrush brush, double x, double y, double width, double height); + + /// + /// Draws a polygon. + /// + void DrawPolygon(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode); + + /// + /// Draws a pie. + /// + void DrawPie(XPen pen, XBrush brush, double x, double y, double width, double height, double startAngle, double sweepAngle); + + /// + /// Draws a cardinal spline. + /// + void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, double tension, XFillMode fillmode); + + /// + /// Draws a graphical path. + /// + void DrawPath(XPen pen, XBrush brush, XGraphicsPath path); + + /// + /// Draws a series of glyphs identified by the specified text and font. + /// + void DrawString(string s, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format); + + /// + /// Draws an image. + /// + void DrawImage(XImage image, double x, double y, double width, double height); + void DrawImage(XImage image, XRect destRect, XRect srcRect, XGraphicsUnit srcUnit); + + #endregion + + #region Save and Restore + + /// + /// Saves the current graphics state without changing it. + /// + void Save(XGraphicsState state); + + /// + /// Restores the specified graphics state. + /// + void Restore(XGraphicsState state); + + /// + /// + /// + void BeginContainer(XGraphicsContainer container, XRect dstrect, XRect srcrect, XGraphicsUnit unit); + + /// + /// + /// + void EndContainer(XGraphicsContainer container); + + #endregion + + #region Transformation + + /// + /// Gets or sets the transformation matrix. + /// + //XMatrix Transform {get; set;} + + void AddTransform(XMatrix transform, XMatrixOrder matrixOrder); + + #endregion + + #region Clipping + + void SetClip(XGraphicsPath path, XCombineMode combineMode); + + void ResetClip(); + + #endregion + + #region Miscellaneous + + /// + /// Writes a comment to the output stream. Comments have no effect on the rendering of the output. + /// + void WriteComment(string comment); + + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/ImageHelper.cs b/src/PDFsharp/src/PdfSharp/Drawing/ImageHelper.cs new file mode 100644 index 00000000..e52eefd3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/ImageHelper.cs @@ -0,0 +1,136 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Helper class for processing image files. + /// + static class ImageHelper + { +#if WPF && GDI + /// + /// Creates a WPF bitmap source from an GDI image. + /// + public static BitmapSource CreateBitmapSource(Image image) + { + MemoryStream stream = new MemoryStream(); + //int width = image.Width; + //int height = image.Height; + //double dpiX = image.HorizontalResolution; + //double dpiY = image.VerticalResolution; + //System.Windows.Media.PixelFormat pixelformat = PixelFormats.Default; + BitmapSource bitmapSource = null; + + try + { + string guid = image.RawFormat.Guid.ToString("B").ToUpper(); + switch (guid) + { + case "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}": // memoryBMP + case "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}": // bmp + image.Save(stream, ImageFormat.Bmp); + stream.Position = 0; + BmpBitmapDecoder bmpDecoder = new BmpBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + bitmapSource = bmpDecoder.Frames[0]; + break; + + case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": // png + image.Save(stream, ImageFormat.Png); + stream.Position = 0; + PngBitmapDecoder pngDecoder = new PngBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + bitmapSource = pngDecoder.Frames[0]; + break; + + case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg + image.Save(stream, ImageFormat.Jpeg); + JpegBitmapDecoder jpegDecoder = new JpegBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + stream.Position = 0; + bitmapSource = jpegDecoder.Frames[0]; + break; + + case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif + image.Save(stream, ImageFormat.Gif); + GifBitmapDecoder gifDecoder = new GifBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + stream.Position = 0; + bitmapSource = gifDecoder.Frames[0]; + break; + + case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff + image.Save(stream, ImageFormat.Tiff); + TiffBitmapDecoder tiffDecoder = new TiffBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + stream.Position = 0; + bitmapSource = tiffDecoder.Frames[0]; + break; + + case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon + image.Save(stream, ImageFormat.Icon); + IconBitmapDecoder iconDecoder = new IconBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); + stream.Position = 0; + bitmapSource = iconDecoder.Frames[0]; + break; + + case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf + case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf + case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif + case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD + case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX + + default: + throw new InvalidOperationException("Unsupported image format."); + } + } + catch (Exception ex) + { + Debug.WriteLine("ImageHelper.CreateBitmapSource failed:" + ex.Message); + } + finally + { + //if (stream != null) + // stream.Close(); + } + return bitmapSource; + } +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/InternalGraphicsState.cs b/src/PDFsharp/src/PdfSharp/Drawing/InternalGraphicsState.cs new file mode 100644 index 00000000..71940cb6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/InternalGraphicsState.cs @@ -0,0 +1,192 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + // In GDI+ the functions Save/Restore, BeginContainer/EndContainer, Transform, SetClip and ResetClip + // can be combined in any order. E.g. you can set a clip region, save the graphics state, empty the + // clip region and draw without clipping. Then you can restore to the previous clip region. With PDF + // this behavior is hard to implement. To solve this problem I first an automaton that keeps track + // of all clipping paths and the current transformation when the clip path was set. The automation + // manages a PDF graphics state stack to calculate the desired bahaviour. It also takes into consideration + // not to multiply with inverse matrixes when the user sets a new transformation matrix. + // After the design works on pager I decided not to implement it because it is much to large-scale. + // Instead I lay down some rules how to use the XGraphics class. + // + // * Before you set a transformation matrix save the graphics state (Save) or begin a new container + // (BeginContainer). + // + // * Instead of resetting the transformation matrix, call Restore or EndContainer. If you reset the + // transformation, in PDF must be multiplied with the inverse matrix. That leads to round off errors + // because in PDF file only 3 digits are used and Acrobat internally uses fixed point numbers (until + // versioin 6 or 7 I think). + // + // * When no clip path is defined, you can set or intersect a new path. + // + // * When a clip path is already defined, you can always intersect with a new one (wich leads in general + // to a smaller clip region). + // + // * When a clip path is already defined, you can only reset it to the empty region (ResetClip) when + // the graphics state stack is at the same position as it had when the clip path was defined. Otherwise + // an error occurs. + // + // Keeping these rules leads to easy to read code and best results in PDF output. + + /// + /// Represents the internal state of an XGraphics object. + /// Used when the state is saved and restored. + /// + internal class InternalGraphicsState + { + public InternalGraphicsState(XGraphics gfx) + { + _gfx = gfx; + } + + public InternalGraphicsState(XGraphics gfx, XGraphicsState state) + { + _gfx = gfx; + State = state; + State.InternalState = this; + } + + public InternalGraphicsState(XGraphics gfx, XGraphicsContainer container) + { + _gfx = gfx; + container.InternalState = this; + } + + /// + /// Gets or sets the current transformation matrix. + /// + public XMatrix Transform + { + get { return _transform; } + set { _transform = value; } + } + XMatrix _transform; + + /// + /// Called after this instanced was pushed on the internal graphics stack. + /// + public void Pushed() + { +#if GDI + // Nothing to do. +#endif +#if WPF && !SILVERLIGHT + // Nothing to do. +#endif +#if SILVERLIGHT + // Save current level of Canvas stack. + _stackLevel = _gfx._dc.Level; + // Create new Canvas for subsequent UIElements. + _gfx._dc.PushCanvas(); +#endif + } + + /// + /// Called after this instanced was popped from the internal graphics stack. + /// + public void Popped() + { + Invalid = true; +#if GDI + // Nothing to do. +#endif +#if WPF && !SILVERLIGHT + // Pop all objects pushed in this state. + if (_gfx.TargetContext == XGraphicTargetContext.WPF) + { + for (int idx = 0; idx < _transformPushLevel; idx++) + _gfx._dc.Pop(); + _transformPushLevel = 0; + for (int idx = 0; idx < _geometryPushLevel; idx++) + _gfx._dc.Pop(); + _geometryPushLevel = 0; + } +#endif +#if SILVERLIGHT + // Pop all Canvas objects created in this state. + _gfx._dc.Pop(_gfx._dc.Level - _stackLevel); +#endif + } + + public bool Invalid; + +#if GDI_ + /// + /// The GDI+ GraphicsState if contructed from XGraphicsState. + /// + public GraphicsState GdiGraphicsState; +#endif + +#if WPF && !SILVERLIGHT + public void PushTransform(MatrixTransform transform) + { + _gfx._dc.PushTransform(transform); + _transformPushLevel++; + } + int _transformPushLevel; + + public void PushClip(Geometry geometry) + { + _gfx._dc.PushClip(geometry); + _geometryPushLevel++; + } + int _geometryPushLevel; +#endif + +#if SILVERLIGHT + public void PushTransform(MatrixTransform transform) + { + _gfx._dc.PushTransform(transform); + } + + public void PushClip(Geometry geometry) + { + _gfx._dc.PushClip(geometry); + } + + int _stackLevel; +#endif + + readonly XGraphics _gfx; + + internal XGraphicsState State; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/PdfFontOptions.cs b/src/PDFsharp/src/PdfSharp/Drawing/PdfFontOptions.cs new file mode 100644 index 00000000..deeff52e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/PdfFontOptions.cs @@ -0,0 +1,108 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Pdf; + +namespace PdfSharp.Drawing +{ + /// + /// Specifies details about how the font is used in PDF creation. + /// + public class XPdfFontOptions + { + internal XPdfFontOptions() { } + + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Must not specify an embedding option anymore.")] + public XPdfFontOptions(PdfFontEncoding encoding, PdfFontEmbedding embedding) + { + _fontEncoding = encoding; + } + + /// + /// Initializes a new instance of the class. + /// + public XPdfFontOptions(PdfFontEncoding encoding) + { + _fontEncoding = encoding; + } + + /// + /// Initializes a new instance of the class. + /// + [Obsolete("Must not specify an embedding option anymore.")] + public XPdfFontOptions(PdfFontEmbedding embedding) + { + _fontEncoding = PdfFontEncoding.WinAnsi; + } + + /// + /// Gets a value indicating the font embedding. + /// + public PdfFontEmbedding FontEmbedding + { + get { return PdfFontEmbedding.Always; } + } + + /// + /// Gets a value indicating how the font is encoded. + /// + public PdfFontEncoding FontEncoding + { + get { return _fontEncoding; } + } + readonly PdfFontEncoding _fontEncoding; + + /// + /// Gets the default options with WinAnsi encoding and always font embedding. + /// + public static XPdfFontOptions WinAnsiDefault + { + get { return new XPdfFontOptions(PdfFontEncoding.WinAnsi); } + } + + /// + /// Gets the default options with Unicode encoding and always font embedding. + /// + public static XPdfFontOptions UnicodeDefault + { + get { return new XPdfFontOptions(PdfFontEncoding.Unicode); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBitmapDecoder.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapDecoder.cs new file mode 100644 index 00000000..ce7a0b8b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapDecoder.cs @@ -0,0 +1,70 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if CORE +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media.Imaging; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Provides functionality to load a bitmap image encoded in a specific format. + /// + public class XBitmapDecoder + { + internal XBitmapDecoder() + { } + + /// + /// Gets a new instance of the PNG image decoder. + /// + public static XBitmapDecoder GetPngDecoder() + { + return new XPngBitmapDecoder(); + } + } + + internal sealed class XPngBitmapDecoder : XBitmapDecoder + { + internal XPngBitmapDecoder() + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBitmapEncoder.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapEncoder.cs new file mode 100644 index 00000000..363d72e7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapEncoder.cs @@ -0,0 +1,122 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Internal; +#if CORE +#endif +#if CORE_WITH_GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media.Imaging; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Provides functionality to save a bitmap image in a specific format. + /// + public abstract class XBitmapEncoder + { + internal XBitmapEncoder() + { + // Prevent external deriving. + } + + /// + /// Gets a new instance of the PNG image encoder. + /// + public static XBitmapEncoder GetPngEncoder() + { + return new XPngBitmapEncoder(); + } + + /// + /// Gets or sets the bitmap source to be encoded. + /// + public XBitmapSource Source + { + get { return _source; } + set { _source = value; } + } + XBitmapSource _source; + + /// + /// When overridden in a derived class saves the image on the specified stream + /// in the respective format. + /// + public abstract void Save(Stream stream); + } + + internal sealed class XPngBitmapEncoder : XBitmapEncoder + { + internal XPngBitmapEncoder() + { } + + /// + /// Saves the image on the specified stream in PNG format. + /// + public override void Save(Stream stream) + { + if (Source == null) + throw new InvalidOperationException("No image source."); +#if CORE_WITH_GDI || GDI + if (Source.AssociatedGraphics != null) + { + Source.DisassociateWithGraphics(); + Debug.Assert(Source.AssociatedGraphics == null); + } + try + { + Lock.EnterGdiPlus(); + Source._gdiImage.Save(stream, ImageFormat.Png); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + DiagnosticsHelper.ThrowNotImplementedException("Save..."); +#endif + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBitmapImage.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapImage.cs new file mode 100644 index 00000000..e64a78b7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapImage.cs @@ -0,0 +1,109 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if CORE +#endif +#if CORE_WITH_GDI +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using PdfSharp.Internal; + +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using PdfSharp.Internal; + +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#if !GDI +using PdfSharp.Internal; +#endif + +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media.Imaging; +using PdfSharp.Internal; + +#endif + +// WPFHACK +#pragma warning disable 0169 +#pragma warning disable 0649 + +namespace PdfSharp.Drawing +{ + /// + /// Defines a pixel based bitmap image. + /// + public sealed class XBitmapImage : XBitmapSource + { + // TODO: Move code from XImage to this class. + + /// + /// Initializes a new instance of the class. + /// + internal XBitmapImage(int width, int height) + { +#if GDI || CORE_WITH_GDI + try + { + Lock.EnterGdiPlus(); + // Create a default 24 bit ARGB bitmap. + _gdiImage = new Bitmap(width, height); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + DiagnosticsHelper.ThrowNotImplementedException("CreateBitmap"); +#endif +#if NETFX_CORE + DiagnosticsHelper.ThrowNotImplementedException("CreateBitmap"); +#endif +#if CORE || GDI && !WPF // Prevent unreachable code error + Initialize(); +#endif + } + + /// + /// Creates a default 24 bit ARGB bitmap with the specified pixel size. + /// + public static XBitmapSource CreateBitmap(int width, int height) + { + // Create a default 24 bit ARGB bitmap. + return new XBitmapImage(width, height); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBitmapSource.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapSource.cs new file mode 100644 index 00000000..c4e4e752 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBitmapSource.cs @@ -0,0 +1,123 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if CORE +#endif + +using System.Diagnostics; +using PdfSharp.Internal; + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media.Imaging; +#endif + +// WPFHACK +#pragma warning disable 0169 +#pragma warning disable 0649 + +namespace PdfSharp.Drawing +{ + /// + /// Defines an abstract base class for pixel based images. + /// + public abstract class XBitmapSource : XImage + { + // TODO: Move code from XImage to this class. + + /// + /// Gets the width of the image in pixels. + /// + public override int PixelWidth + { + get + { +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Width; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + int gdiWidth = _gdiImage.Width; + int wpfWidth = _wpfImage.PixelWidth; + Debug.Assert(gdiWidth == wpfWidth); + return wpfWidth; +#endif +#if WPF && !GDI + return _wpfImage.PixelWidth; +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelWidth; +#endif + } + } + + /// + /// Gets the height of the image in pixels. + /// + public override int PixelHeight + { + get + { +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Height; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + int gdiHeight = _gdiImage.Height; + int wpfHeight = _wpfImage.PixelHeight; + Debug.Assert(gdiHeight == wpfHeight); + return wpfHeight; +#endif +#if WPF && !GDI + return _wpfImage.PixelHeight; +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelHeight; +#endif + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBrush.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBrush.cs new file mode 100644 index 00000000..0822170e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBrush.cs @@ -0,0 +1,87 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif +#if UWP +using Microsoft.Graphics.Canvas.Brushes; +using UwpColor = Windows.UI.Color; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Classes derived from this abstract base class define objects used to fill the + /// interiors of paths. + /// + public abstract class XBrush + { +#if GDI + internal abstract System.Drawing.Brush RealizeGdiBrush(); + +#if UseGdiObjects + /// + /// Converts from a System.Drawing.Brush. + /// + public static implicit operator XBrush(Brush brush) + { + XBrush xbrush; + SolidBrush solidBrush; + LinearGradientBrush lgBrush; + if ((solidBrush = brush as SolidBrush) != null) + { + xbrush = new XSolidBrush(solidBrush.Color); + } + else if ((lgBrush = brush as LinearGradientBrush) != null) + { + // TODO: xbrush = new LinearGradientBrush(lgBrush.Rectangle, lgBrush.co(solidBrush.Color); + throw new NotImplementedException("Brush type not yet supported by PDFsharp."); + } + else + { + throw new NotImplementedException("Brush type not supported by PDFsharp."); + } + return xbrush; + } +#endif +#endif +#if WPF + internal abstract System.Windows.Media.Brush RealizeWpfBrush(); +#endif +#if UWP + internal abstract ICanvasBrush RealizeCanvasBrush(); +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XBrushes.cs b/src/PDFsharp/src/PdfSharp/Drawing/XBrushes.cs new file mode 100644 index 00000000..8a603f35 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XBrushes.cs @@ -0,0 +1,1595 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable UnusedMember.Global + +#define USE_CACHE_is_not_thread_safe + +namespace PdfSharp.Drawing +{ + /// + /// Brushes for all the pre-defined colors. + /// + public static class XBrushes + { + /// Gets a pre-defined XBrush object. + public static XSolidBrush AliceBlue + { +#if USE_CACHE + get { return _aliceBlue ?? (_aliceBlue = new XSolidBrush(XColors.AliceBlue, true)); } +#else + get { return new XSolidBrush(XColors.AliceBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush AntiqueWhite + { +#if USE_CACHE + get { return _antiqueWhite ?? (_antiqueWhite = new XSolidBrush(XColors.AntiqueWhite, true)); } +#else + get { return new XSolidBrush(XColors.AntiqueWhite, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Aqua + { +#if USE_CACHE + get { return _aqua ?? (_aqua = new XSolidBrush(XColors.Aqua, true)); } +#else + get { return new XSolidBrush(XColors.Aqua, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Aquamarine + { +#if USE_CACHE + get { return _aquamarine ?? (_aquamarine = new XSolidBrush(XColors.Aquamarine, true)); } +#else + get { return new XSolidBrush(XColors.Aquamarine, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Azure + { +#if USE_CACHE + get { return _azure ?? (_azure = new XSolidBrush(XColors.Azure, true)); } +#else + get { return new XSolidBrush(XColors.Azure, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Beige + { +#if USE_CACHE + get { return _beige ?? (_beige = new XSolidBrush(XColors.Beige, true)); } +#else + get { return new XSolidBrush(XColors.Beige, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Bisque + { +#if USE_CACHE + get { return _bisque ?? (_bisque = new XSolidBrush(XColors.Bisque, true)); } +#else + get { return new XSolidBrush(XColors.Bisque, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Black + { +#if USE_CACHE + get { return _black ?? (_black = new XSolidBrush(XColors.Black, true)); } +#else + get { return new XSolidBrush(XColors.Black, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush BlanchedAlmond + { +#if USE_CACHE + get { return _blanchedAlmond ?? (_blanchedAlmond = new XSolidBrush(XColors.BlanchedAlmond, true)); } +#else + get { return new XSolidBrush(XColors.BlanchedAlmond, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Blue + { +#if USE_CACHE + get { return _blue ?? (_blue = new XSolidBrush(XColors.Blue, true)); } +#else + get { return new XSolidBrush(XColors.Blue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush BlueViolet + { +#if USE_CACHE + get { return _blueViolet ?? (_blueViolet = new XSolidBrush(XColors.BlueViolet, true)); } +#else + get { return new XSolidBrush(XColors.BlueViolet, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Brown + { +#if USE_CACHE + get { return _brown ?? (_brown = new XSolidBrush(XColors.Brown, true)); } +#else + get { return new XSolidBrush(XColors.Brown, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush BurlyWood + { +#if USE_CACHE + get { return _burlyWood ?? (_burlyWood = new XSolidBrush(XColors.BurlyWood, true)); } +#else + get { return new XSolidBrush(XColors.BurlyWood, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush CadetBlue + { +#if USE_CACHE + get { return _cadetBlue ?? (_cadetBlue = new XSolidBrush(XColors.CadetBlue, true)); } +#else + get { return new XSolidBrush(XColors.CadetBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Chartreuse + { +#if USE_CACHE + get { return _chartreuse ?? (_chartreuse = new XSolidBrush(XColors.Chartreuse, true)); } +#else + get { return new XSolidBrush(XColors.Chartreuse, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Chocolate + { +#if USE_CACHE + get { return _chocolate ?? (_chocolate = new XSolidBrush(XColors.Chocolate, true)); } +#else + get { return new XSolidBrush(XColors.Chocolate, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Coral + { +#if USE_CACHE + get { return _coral ?? (_coral = new XSolidBrush(XColors.Coral, true)); } +#else + get { return new XSolidBrush(XColors.Coral, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush CornflowerBlue + { +#if USE_CACHE + get { return _cornflowerBlue ?? (_cornflowerBlue = new XSolidBrush(XColors.CornflowerBlue, true)); } +#else + get { return new XSolidBrush(XColors.CornflowerBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Cornsilk + { +#if USE_CACHE + get { return _cornsilk ?? (_cornsilk = new XSolidBrush(XColors.Cornsilk, true)); } +#else + get { return new XSolidBrush(XColors.Cornsilk, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Crimson + { +#if USE_CACHE + get { return _crimson ?? (_crimson = new XSolidBrush(XColors.Crimson, true)); } +#else + get { return new XSolidBrush(XColors.Crimson, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Cyan + { +#if USE_CACHE + get { return _cyan ?? (_cyan = new XSolidBrush(XColors.Cyan, true)); } +#else + get { return new XSolidBrush(XColors.Cyan, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkBlue + { +#if USE_CACHE + get { return _darkBlue ?? (_darkBlue = new XSolidBrush(XColors.DarkBlue, true)); } +#else + get { return new XSolidBrush(XColors.DarkBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkCyan + { +#if USE_CACHE + get { return _darkCyan ?? (_darkCyan = new XSolidBrush(XColors.DarkCyan, true)); } +#else + get { return new XSolidBrush(XColors.DarkCyan, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkGoldenrod + { +#if USE_CACHE + get { return _darkGoldenrod ?? (_darkGoldenrod = new XSolidBrush(XColors.DarkGoldenrod, true)); } +#else + get { return new XSolidBrush(XColors.DarkGoldenrod, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkGray + { +#if USE_CACHE + get { return _darkGray ?? (_darkGray = new XSolidBrush(XColors.DarkGray, true)); } +#else + get { return new XSolidBrush(XColors.DarkGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkGreen + { +#if USE_CACHE + get { return _darkGreen ?? (_darkGreen = new XSolidBrush(XColors.DarkGreen, true)); } +#else + get { return new XSolidBrush(XColors.DarkGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkKhaki + { +#if USE_CACHE + get { return _darkKhaki ?? (_darkKhaki = new XSolidBrush(XColors.DarkKhaki, true)); } +#else + get { return new XSolidBrush(XColors.DarkKhaki, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkMagenta + { +#if USE_CACHE + get { return _darkMagenta ?? (_darkMagenta = new XSolidBrush(XColors.DarkMagenta, true)); } +#else + get { return new XSolidBrush(XColors.DarkMagenta, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkOliveGreen + { +#if USE_CACHE + get { return _darkOliveGreen ?? (_darkOliveGreen = new XSolidBrush(XColors.DarkOliveGreen, true)); } +#else + get { return new XSolidBrush(XColors.DarkOliveGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkOrange + { +#if USE_CACHE + get { return _darkOrange ?? (_darkOrange = new XSolidBrush(XColors.DarkOrange, true)); } +#else + get { return new XSolidBrush(XColors.DarkOrange, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkOrchid + { +#if USE_CACHE + get { return _darkOrchid ?? (_darkOrchid = new XSolidBrush(XColors.DarkOrchid, true)); } +#else + get { return new XSolidBrush(XColors.DarkOrchid, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkRed + { +#if USE_CACHE + get { return _darkRed ?? (_darkRed = new XSolidBrush(XColors.DarkRed, true)); } +#else + get { return new XSolidBrush(XColors.DarkRed, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkSalmon + { +#if USE_CACHE + get { return _darkSalmon ?? (_darkSalmon = new XSolidBrush(XColors.DarkSalmon, true)); } +#else + get { return new XSolidBrush(XColors.DarkSalmon, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkSeaGreen + { +#if USE_CACHE + get { return _darkSeaGreen ?? (_darkSeaGreen = new XSolidBrush(XColors.DarkSeaGreen, true)); } +#else + get { return new XSolidBrush(XColors.DarkSeaGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkSlateBlue + { +#if USE_CACHE + get { return _darkSlateBlue ?? (_darkSlateBlue = new XSolidBrush(XColors.DarkSlateBlue, true)); } +#else + get { return new XSolidBrush(XColors.DarkSlateBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkSlateGray + { +#if USE_CACHE + get { return _darkSlateGray ?? (_darkSlateGray = new XSolidBrush(XColors.DarkSlateGray, true)); } +#else + get { return new XSolidBrush(XColors.DarkSlateGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkTurquoise + { +#if USE_CACHE + get { return _darkTurquoise ?? (_darkTurquoise = new XSolidBrush(XColors.DarkTurquoise, true)); } +#else + get { return new XSolidBrush(XColors.DarkTurquoise, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DarkViolet + { +#if USE_CACHE + get { return _darkViolet ?? (_darkViolet = new XSolidBrush(XColors.DarkViolet, true)); } +#else + get { return new XSolidBrush(XColors.DarkViolet, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DeepPink + { +#if USE_CACHE + get { return _deepPink ?? (_deepPink = new XSolidBrush(XColors.DeepPink, true)); } +#else + get { return new XSolidBrush(XColors.DeepPink, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DeepSkyBlue + { +#if USE_CACHE + get { return _deepSkyBlue ?? (_deepSkyBlue = new XSolidBrush(XColors.DeepSkyBlue, true)); } +#else + get { return new XSolidBrush(XColors.DeepSkyBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DimGray + { +#if USE_CACHE + get { return _dimGray ?? (_dimGray = new XSolidBrush(XColors.DimGray, true)); } +#else + get { return new XSolidBrush(XColors.DimGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush DodgerBlue + { +#if USE_CACHE + get { return _dodgerBlue ?? (_dodgerBlue = new XSolidBrush(XColors.DodgerBlue, true)); } +#else + get { return new XSolidBrush(XColors.DodgerBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Firebrick + { +#if USE_CACHE + get { return _firebrick ?? (_firebrick = new XSolidBrush(XColors.Firebrick, true)); } +#else + get { return new XSolidBrush(XColors.Firebrick, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush FloralWhite + { +#if USE_CACHE + get { return _floralWhite ?? (_floralWhite = new XSolidBrush(XColors.FloralWhite, true)); } +#else + get { return new XSolidBrush(XColors.FloralWhite, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush ForestGreen + { +#if USE_CACHE + get { return _forestGreen ?? (_forestGreen = new XSolidBrush(XColors.ForestGreen, true)); } +#else + get { return new XSolidBrush(XColors.ForestGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Fuchsia + { +#if USE_CACHE + get { return _fuchsia ?? (_fuchsia = new XSolidBrush(XColors.Fuchsia, true)); } +#else + get { return new XSolidBrush(XColors.Fuchsia, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Gainsboro + { +#if USE_CACHE + get { return _gainsboro ?? (_gainsboro = new XSolidBrush(XColors.Gainsboro, true)); } +#else + get { return new XSolidBrush(XColors.Gainsboro, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush GhostWhite + { +#if USE_CACHE + get { return _ghostWhite ?? (_ghostWhite = new XSolidBrush(XColors.GhostWhite, true)); } +#else + get { return new XSolidBrush(XColors.GhostWhite, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Gold + { +#if USE_CACHE + get { return _gold ?? (_gold = new XSolidBrush(XColors.Gold, true)); } +#else + get { return new XSolidBrush(XColors.Gold, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Goldenrod + { +#if USE_CACHE + get { return _goldenrod ?? (_goldenrod = new XSolidBrush(XColors.Goldenrod, true)); } +#else + get { return new XSolidBrush(XColors.Goldenrod, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Gray + { +#if USE_CACHE + get { return _gray ?? (_gray = new XSolidBrush(XColors.Gray, true)); } +#else + get { return new XSolidBrush(XColors.Gray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Green + { +#if USE_CACHE + get { return _green ?? (_green = new XSolidBrush(XColors.Green, true)); } +#else + get { return new XSolidBrush(XColors.Green, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush GreenYellow + { +#if USE_CACHE + get { return _greenYellow ?? (_greenYellow = new XSolidBrush(XColors.GreenYellow, true)); } +#else + get { return new XSolidBrush(XColors.GreenYellow, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Honeydew + { +#if USE_CACHE + get { return _honeydew ?? (_honeydew = new XSolidBrush(XColors.Honeydew, true)); } +#else + get { return new XSolidBrush(XColors.Honeydew, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush HotPink + { +#if USE_CACHE + get { return _hotPink ?? (_hotPink = new XSolidBrush(XColors.HotPink, true)); } +#else + get { return new XSolidBrush(XColors.HotPink, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush IndianRed + { +#if USE_CACHE + get { return _indianRed ?? (_indianRed = new XSolidBrush(XColors.IndianRed, true)); } +#else + get { return new XSolidBrush(XColors.IndianRed, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Indigo + { +#if USE_CACHE + get { return _indigo ?? (_indigo = new XSolidBrush(XColors.Indigo, true)); } +#else + get { return new XSolidBrush(XColors.Indigo, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Ivory + { +#if USE_CACHE + get { return _ivory ?? (_ivory = new XSolidBrush(XColors.Ivory, true)); } +#else + get { return new XSolidBrush(XColors.Ivory, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Khaki + { +#if USE_CACHE + get { return _khaki ?? (_khaki = new XSolidBrush(XColors.Khaki, true)); } +#else + get { return new XSolidBrush(XColors.Khaki, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Lavender + { +#if USE_CACHE + get { return _lavender ?? (_lavender = new XSolidBrush(XColors.Lavender, true)); } +#else + get { return new XSolidBrush(XColors.Lavender, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LavenderBlush + { +#if USE_CACHE + get { return _lavenderBlush ?? (_lavenderBlush = new XSolidBrush(XColors.LavenderBlush, true)); } +#else + get { return new XSolidBrush(XColors.LavenderBlush, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LawnGreen + { +#if USE_CACHE + get { return _lawnGreen ?? (_lawnGreen = new XSolidBrush(XColors.LawnGreen, true)); } +#else + get { return new XSolidBrush(XColors.LawnGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LemonChiffon + { +#if USE_CACHE + get { return _lemonChiffon ?? (_lemonChiffon = new XSolidBrush(XColors.LemonChiffon, true)); } +#else + get { return new XSolidBrush(XColors.LemonChiffon, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightBlue + { +#if USE_CACHE + get { return _lightBlue ?? (_lightBlue = new XSolidBrush(XColors.LightBlue, true)); } +#else + get { return new XSolidBrush(XColors.LightBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightCoral + { +#if USE_CACHE + get { return _lightCoral ?? (_lightCoral = new XSolidBrush(XColors.LightCoral, true)); } +#else + get { return new XSolidBrush(XColors.LightCoral, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightCyan + { +#if USE_CACHE + get { return _lightCyan ?? (_lightCyan = new XSolidBrush(XColors.LightCyan, true)); } +#else + get { return new XSolidBrush(XColors.LightCyan, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightGoldenrodYellow + { +#if USE_CACHE + get { return _lightGoldenrodYellow ?? (_lightGoldenrodYellow = new XSolidBrush(XColors.LightGoldenrodYellow, true)); } +#else + get { return new XSolidBrush(XColors.LightGoldenrodYellow, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightGray + { +#if USE_CACHE + get { return _lightGray ?? (_lightGray = new XSolidBrush(XColors.LightGray, true)); } +#else + get { return new XSolidBrush(XColors.LightGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightGreen + { +#if USE_CACHE + get { return _lightGreen ?? (_lightGreen = new XSolidBrush(XColors.LightGreen, true)); } +#else + get { return new XSolidBrush(XColors.LightGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightPink + { +#if USE_CACHE + get { return _lightPink ?? (_lightPink = new XSolidBrush(XColors.LightPink, true)); } +#else + get { return new XSolidBrush(XColors.LightPink, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightSalmon + { +#if USE_CACHE + get { return _lightSalmon ?? (_lightSalmon = new XSolidBrush(XColors.LightSalmon, true)); } +#else + get { return new XSolidBrush(XColors.LightSalmon, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightSeaGreen + { +#if USE_CACHE + get { return _lightSeaGreen ?? (_lightSeaGreen = new XSolidBrush(XColors.LightSeaGreen, true)); } +#else + get { return new XSolidBrush(XColors.LightSeaGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightSkyBlue + { +#if USE_CACHE + get { return _lightSkyBlue ?? (_lightSkyBlue = new XSolidBrush(XColors.LightSkyBlue, true)); } +#else + get { return new XSolidBrush(XColors.LightSkyBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightSlateGray + { +#if USE_CACHE + get { return _lightSlateGray ?? (_lightSlateGray = new XSolidBrush(XColors.LightSlateGray, true)); } +#else + get { return new XSolidBrush(XColors.LightSlateGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightSteelBlue + { +#if USE_CACHE + get { return _lightSteelBlue ?? (_lightSteelBlue = new XSolidBrush(XColors.LightSteelBlue, true)); } +#else + get { return new XSolidBrush(XColors.LightSteelBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LightYellow + { +#if USE_CACHE + get { return _lightYellow ?? (_lightYellow = new XSolidBrush(XColors.LightYellow, true)); } +#else + get { return new XSolidBrush(XColors.LightYellow, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Lime + { +#if USE_CACHE + get { return _lime ?? (_lime = new XSolidBrush(XColors.Lime, true)); } +#else + get { return new XSolidBrush(XColors.Lime, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush LimeGreen + { +#if USE_CACHE + get { return _limeGreen ?? (_limeGreen = new XSolidBrush(XColors.LimeGreen, true)); } +#else + get { return new XSolidBrush(XColors.LimeGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Linen + { +#if USE_CACHE + get { return _linen ?? (_linen = new XSolidBrush(XColors.Linen, true)); } +#else + get { return new XSolidBrush(XColors.Linen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Magenta + { +#if USE_CACHE + get { return _magenta ?? (_magenta = new XSolidBrush(XColors.Magenta, true)); } +#else + get { return new XSolidBrush(XColors.Magenta, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Maroon + { +#if USE_CACHE + get { return _maroon ?? (_maroon = new XSolidBrush(XColors.Maroon, true)); } +#else + get { return new XSolidBrush(XColors.Maroon, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumAquamarine + { +#if USE_CACHE + get { return _mediumAquamarine ?? (_mediumAquamarine = new XSolidBrush(XColors.MediumAquamarine, true)); } +#else + get { return new XSolidBrush(XColors.MediumAquamarine, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumBlue + { +#if USE_CACHE + get { return _mediumBlue ?? (_mediumBlue = new XSolidBrush(XColors.MediumBlue, true)); } +#else + get { return new XSolidBrush(XColors.MediumBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumOrchid + { +#if USE_CACHE + get { return _mediumOrchid ?? (_mediumOrchid = new XSolidBrush(XColors.MediumOrchid, true)); } +#else + get { return new XSolidBrush(XColors.MediumOrchid, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumPurple + { +#if USE_CACHE + get { return _mediumPurple ?? (_mediumPurple = new XSolidBrush(XColors.MediumPurple, true)); } +#else + get { return new XSolidBrush(XColors.MediumPurple, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumSeaGreen + { +#if USE_CACHE + get { return _mediumSeaGreen ?? (_mediumSeaGreen = new XSolidBrush(XColors.MediumSeaGreen, true)); } +#else + get { return new XSolidBrush(XColors.MediumSeaGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumSlateBlue + { +#if USE_CACHE + get { return _mediumSlateBlue ?? (_mediumSlateBlue = new XSolidBrush(XColors.MediumSlateBlue, true)); } +#else + get { return new XSolidBrush(XColors.MediumSlateBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumSpringGreen + { +#if USE_CACHE + get { return _mediumSpringGreen ?? (_mediumSpringGreen = new XSolidBrush(XColors.MediumSpringGreen, true)); } +#else + get { return new XSolidBrush(XColors.MediumSpringGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumTurquoise + { +#if USE_CACHE + get { return _mediumTurquoise ?? (_mediumTurquoise = new XSolidBrush(XColors.MediumTurquoise, true)); } +#else + get { return new XSolidBrush(XColors.MediumTurquoise, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MediumVioletRed + { +#if USE_CACHE + get { return _mediumVioletRed ?? (_mediumVioletRed = new XSolidBrush(XColors.MediumVioletRed, true)); } +#else + get { return new XSolidBrush(XColors.MediumVioletRed, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MidnightBlue + { +#if USE_CACHE + get { return _midnightBlue ?? (_midnightBlue = new XSolidBrush(XColors.MidnightBlue, true)); } +#else + get { return new XSolidBrush(XColors.MidnightBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MintCream + { +#if USE_CACHE + get { return _mintCream ?? (_mintCream = new XSolidBrush(XColors.MintCream, true)); } +#else + get { return new XSolidBrush(XColors.MintCream, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush MistyRose + { +#if USE_CACHE + get { return _mistyRose ?? (_mistyRose = new XSolidBrush(XColors.MistyRose, true)); } +#else + get { return new XSolidBrush(XColors.MistyRose, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Moccasin + { +#if USE_CACHE + get { return _moccasin ?? (_moccasin = new XSolidBrush(XColors.Moccasin, true)); } +#else + get { return new XSolidBrush(XColors.Moccasin, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush NavajoWhite + { +#if USE_CACHE + get { return _navajoWhite ?? (_navajoWhite = new XSolidBrush(XColors.NavajoWhite, true)); } +#else + get { return new XSolidBrush(XColors.NavajoWhite, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Navy + { +#if USE_CACHE + get { return _navy ?? (_navy = new XSolidBrush(XColors.Navy, true)); } +#else + get { return new XSolidBrush(XColors.Navy, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush OldLace + { +#if USE_CACHE + get { return _oldLace ?? (_oldLace = new XSolidBrush(XColors.OldLace, true)); } +#else + get { return new XSolidBrush(XColors.OldLace, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Olive + { +#if USE_CACHE + get { return _olive ?? (_olive = new XSolidBrush(XColors.Olive, true)); } +#else + get { return new XSolidBrush(XColors.Olive, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush OliveDrab + { +#if USE_CACHE + get { return _oliveDrab ?? (_oliveDrab = new XSolidBrush(XColors.OliveDrab, true)); } +#else + get { return new XSolidBrush(XColors.OliveDrab, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Orange + { +#if USE_CACHE + get { return _orange ?? (_orange = new XSolidBrush(XColors.Orange, true)); } +#else + get { return new XSolidBrush(XColors.Orange, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush OrangeRed + { +#if USE_CACHE + get { return _orangeRed ?? (_orangeRed = new XSolidBrush(XColors.OrangeRed, true)); } +#else + get { return new XSolidBrush(XColors.OrangeRed, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Orchid + { +#if USE_CACHE + get { return _orchid ?? (_orchid = new XSolidBrush(XColors.Orchid, true)); } +#else + get { return new XSolidBrush(XColors.Orchid, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PaleGoldenrod + { +#if USE_CACHE + get { return _paleGoldenrod ?? (_paleGoldenrod = new XSolidBrush(XColors.PaleGoldenrod, true)); } +#else + get { return new XSolidBrush(XColors.PaleGoldenrod, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PaleGreen + { +#if USE_CACHE + get { return _paleGreen ?? (_paleGreen = new XSolidBrush(XColors.PaleGreen, true)); } +#else + get { return new XSolidBrush(XColors.PaleGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PaleTurquoise + { +#if USE_CACHE + get { return _paleTurquoise ?? (_paleTurquoise = new XSolidBrush(XColors.PaleTurquoise, true)); } +#else + get { return new XSolidBrush(XColors.PaleTurquoise, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PaleVioletRed + { +#if USE_CACHE + get { return _paleVioletRed ?? (_paleVioletRed = new XSolidBrush(XColors.PaleVioletRed, true)); } +#else + get { return new XSolidBrush(XColors.PaleVioletRed, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PapayaWhip + { +#if USE_CACHE + get { return _papayaWhip ?? (_papayaWhip = new XSolidBrush(XColors.PapayaWhip, true)); } +#else + get { return new XSolidBrush(XColors.PapayaWhip, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PeachPuff + { +#if USE_CACHE + get { return _peachPuff ?? (_peachPuff = new XSolidBrush(XColors.PeachPuff, true)); } +#else + get { return new XSolidBrush(XColors.PeachPuff, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Peru + { +#if USE_CACHE + get { return _peru ?? (_peru = new XSolidBrush(XColors.Peru, true)); } +#else + get { return new XSolidBrush(XColors.Peru, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Pink + { +#if USE_CACHE + get { return _pink ?? (_pink = new XSolidBrush(XColors.Pink, true)); } +#else + get { return new XSolidBrush(XColors.Pink, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Plum + { +#if USE_CACHE + get { return _plum ?? (_plum = new XSolidBrush(XColors.Plum, true)); } +#else + get { return new XSolidBrush(XColors.Plum, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush PowderBlue + { +#if USE_CACHE + get { return _powderBlue ?? (_powderBlue = new XSolidBrush(XColors.PowderBlue, true)); } +#else + get { return new XSolidBrush(XColors.PowderBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Purple + { +#if USE_CACHE + get { return _purple ?? (_purple = new XSolidBrush(XColors.Purple, true)); } +#else + get { return new XSolidBrush(XColors.Purple, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Red + { +#if USE_CACHE + get { return _red ?? (_red = new XSolidBrush(XColors.Red, true)); } +#else + get { return new XSolidBrush(XColors.Red, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush RosyBrown + { +#if USE_CACHE + get { return _rosyBrown ?? (_rosyBrown = new XSolidBrush(XColors.RosyBrown, true)); } +#else + get { return new XSolidBrush(XColors.RosyBrown, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush RoyalBlue + { +#if USE_CACHE + get { return _royalBlue ?? (_royalBlue = new XSolidBrush(XColors.RoyalBlue, true)); } +#else + get { return new XSolidBrush(XColors.RoyalBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SaddleBrown + { +#if USE_CACHE + get { return _saddleBrown ?? (_saddleBrown = new XSolidBrush(XColors.SaddleBrown, true)); } +#else + get { return new XSolidBrush(XColors.SaddleBrown, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Salmon + { +#if USE_CACHE + get { return _salmon ?? (_salmon = new XSolidBrush(XColors.Salmon, true)); } +#else + get { return new XSolidBrush(XColors.Salmon, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SandyBrown + { +#if USE_CACHE + get { return _sandyBrown ?? (_sandyBrown = new XSolidBrush(XColors.SandyBrown, true)); } +#else + get { return new XSolidBrush(XColors.SandyBrown, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SeaGreen + { +#if USE_CACHE + get { return _seaGreen ?? (_seaGreen = new XSolidBrush(XColors.SeaGreen, true)); } +#else + get { return new XSolidBrush(XColors.SeaGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SeaShell + { +#if USE_CACHE + get { return _seaShell ?? (_seaShell = new XSolidBrush(XColors.SeaShell, true)); } +#else + get { return new XSolidBrush(XColors.SeaShell, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Sienna + { +#if USE_CACHE + get { return _sienna ?? (_sienna = new XSolidBrush(XColors.Sienna, true)); } +#else + get { return new XSolidBrush(XColors.Sienna, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Silver + { +#if USE_CACHE + get { return _silver ?? (_silver = new XSolidBrush(XColors.Silver, true)); } +#else + get { return new XSolidBrush(XColors.Silver, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SkyBlue + { +#if USE_CACHE + get { return _skyBlue ?? (_skyBlue = new XSolidBrush(XColors.SkyBlue, true)); } +#else + get { return new XSolidBrush(XColors.SkyBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SlateBlue + { +#if USE_CACHE + get { return _slateBlue ?? (_slateBlue = new XSolidBrush(XColors.SlateBlue, true)); } +#else + get { return new XSolidBrush(XColors.SlateBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SlateGray + { +#if USE_CACHE + get { return _slateGray ?? (_slateGray = new XSolidBrush(XColors.SlateGray, true)); } +#else + get { return new XSolidBrush(XColors.SlateGray, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Snow + { +#if USE_CACHE + get { return _snow ?? (_snow = new XSolidBrush(XColors.Snow, true)); } +#else + get { return new XSolidBrush(XColors.Snow, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SpringGreen + { +#if USE_CACHE + get { return _springGreen ?? (_springGreen = new XSolidBrush(XColors.SpringGreen, true)); } +#else + get { return new XSolidBrush(XColors.SpringGreen, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush SteelBlue + { +#if USE_CACHE + get { return _steelBlue ?? (_steelBlue = new XSolidBrush(XColors.SteelBlue, true)); } +#else + get { return new XSolidBrush(XColors.SteelBlue, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Tan + { +#if USE_CACHE + get { return _tan ?? (_tan = new XSolidBrush(XColors.Tan, true)); } +#else + get { return new XSolidBrush(XColors.Tan, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Teal + { +#if USE_CACHE + get { return _teal ?? (_teal = new XSolidBrush(XColors.Teal, true)); } +#else + get { return new XSolidBrush(XColors.Teal, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Thistle + { +#if USE_CACHE + get { return _thistle ?? (_thistle = new XSolidBrush(XColors.Thistle, true)); } +#else + get { return new XSolidBrush(XColors.Thistle, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Tomato + { +#if USE_CACHE + get { return _tomato ?? (_tomato = new XSolidBrush(XColors.Tomato, true)); } +#else + get { return new XSolidBrush(XColors.Tomato, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Transparent + { +#if USE_CACHE + get { return _transparent ?? (_transparent = new XSolidBrush(XColors.Transparent, true)); } +#else + get { return new XSolidBrush(XColors.Transparent, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Turquoise + { +#if USE_CACHE + get { return _turquoise ?? (_turquoise = new XSolidBrush(XColors.Turquoise, true)); } +#else + get { return new XSolidBrush(XColors.Turquoise, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Violet + { +#if USE_CACHE + get { return _violet ?? (_violet = new XSolidBrush(XColors.Violet, true)); } +#else + get { return new XSolidBrush(XColors.Violet, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Wheat + { +#if USE_CACHE + get { return _wheat ?? (_wheat = new XSolidBrush(XColors.Wheat, true)); } +#else + get { return new XSolidBrush(XColors.Wheat, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush White + { +#if USE_CACHE + get { return _white ?? (_white = new XSolidBrush(XColors.White, true)); } +#else + get { return new XSolidBrush(XColors.White, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush WhiteSmoke + { +#if USE_CACHE + get { return _whiteSmoke ?? (_whiteSmoke = new XSolidBrush(XColors.WhiteSmoke, true)); } +#else + get { return new XSolidBrush(XColors.WhiteSmoke, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush Yellow + { +#if USE_CACHE + get { return _yellow ?? (_yellow = new XSolidBrush(XColors.Yellow, true)); } +#else + get { return new XSolidBrush(XColors.Yellow, true); } +#endif + } + + /// Gets a pre-defined XBrush object. + public static XSolidBrush YellowGreen + { +#if USE_CACHE + get { return _yellowGreen ?? (_yellowGreen = new XSolidBrush(XColors.YellowGreen, true)); } +#else + get { return new XSolidBrush(XColors.YellowGreen, true); } +#endif + } + +#if USE_CACHE + static XSolidBrush _aliceBlue; + static XSolidBrush _antiqueWhite; + static XSolidBrush _aqua; + static XSolidBrush _aquamarine; + static XSolidBrush _azure; + static XSolidBrush _beige; + static XSolidBrush _bisque; + static XSolidBrush _black; + static XSolidBrush _blanchedAlmond; + static XSolidBrush _blue; + static XSolidBrush _blueViolet; + static XSolidBrush _brown; + static XSolidBrush _burlyWood; + static XSolidBrush _cadetBlue; + static XSolidBrush _chartreuse; + static XSolidBrush _chocolate; + static XSolidBrush _coral; + static XSolidBrush _cornflowerBlue; + static XSolidBrush _cornsilk; + static XSolidBrush _crimson; + static XSolidBrush _cyan; + static XSolidBrush _darkBlue; + static XSolidBrush _darkCyan; + static XSolidBrush _darkGoldenrod; + static XSolidBrush _darkGray; + static XSolidBrush _darkGreen; + static XSolidBrush _darkKhaki; + static XSolidBrush _darkMagenta; + static XSolidBrush _darkOliveGreen; + static XSolidBrush _darkOrange; + static XSolidBrush _darkOrchid; + static XSolidBrush _darkRed; + static XSolidBrush _darkSalmon; + static XSolidBrush _darkSeaGreen; + static XSolidBrush _darkSlateBlue; + static XSolidBrush _darkSlateGray; + static XSolidBrush _darkTurquoise; + static XSolidBrush _darkViolet; + static XSolidBrush _deepPink; + static XSolidBrush _deepSkyBlue; + static XSolidBrush _dimGray; + static XSolidBrush _dodgerBlue; + static XSolidBrush _firebrick; + static XSolidBrush _floralWhite; + static XSolidBrush _forestGreen; + static XSolidBrush _fuchsia; + static XSolidBrush _gainsboro; + static XSolidBrush _ghostWhite; + static XSolidBrush _gold; + static XSolidBrush _goldenrod; + static XSolidBrush _gray; + static XSolidBrush _green; + static XSolidBrush _greenYellow; + static XSolidBrush _honeydew; + static XSolidBrush _hotPink; + static XSolidBrush _indianRed; + static XSolidBrush _indigo; + static XSolidBrush _ivory; + static XSolidBrush _khaki; + static XSolidBrush _lavender; + static XSolidBrush _lavenderBlush; + static XSolidBrush _lawnGreen; + static XSolidBrush _lemonChiffon; + static XSolidBrush _lightBlue; + static XSolidBrush _lightCoral; + static XSolidBrush _lightCyan; + static XSolidBrush _lightGoldenrodYellow; + static XSolidBrush _lightGray; + static XSolidBrush _lightGreen; + static XSolidBrush _lightPink; + static XSolidBrush _lightSalmon; + static XSolidBrush _lightSeaGreen; + static XSolidBrush _lightSkyBlue; + static XSolidBrush _lightSlateGray; + static XSolidBrush _lightSteelBlue; + static XSolidBrush _lightYellow; + static XSolidBrush _lime; + static XSolidBrush _limeGreen; + static XSolidBrush _linen; + static XSolidBrush _magenta; + static XSolidBrush _maroon; + static XSolidBrush _mediumAquamarine; + static XSolidBrush _mediumBlue; + static XSolidBrush _mediumOrchid; + static XSolidBrush _mediumPurple; + static XSolidBrush _mediumSeaGreen; + static XSolidBrush _mediumSlateBlue; + static XSolidBrush _mediumSpringGreen; + static XSolidBrush _mediumTurquoise; + static XSolidBrush _mediumVioletRed; + static XSolidBrush _midnightBlue; + static XSolidBrush _mintCream; + static XSolidBrush _mistyRose; + static XSolidBrush _moccasin; + static XSolidBrush _navajoWhite; + static XSolidBrush _navy; + static XSolidBrush _oldLace; + static XSolidBrush _olive; + static XSolidBrush _oliveDrab; + static XSolidBrush _orange; + static XSolidBrush _orangeRed; + static XSolidBrush _orchid; + static XSolidBrush _paleGoldenrod; + static XSolidBrush _paleGreen; + static XSolidBrush _paleTurquoise; + static XSolidBrush _paleVioletRed; + static XSolidBrush _papayaWhip; + static XSolidBrush _peachPuff; + static XSolidBrush _peru; + static XSolidBrush _pink; + static XSolidBrush _plum; + static XSolidBrush _powderBlue; + static XSolidBrush _purple; + static XSolidBrush _red; + static XSolidBrush _rosyBrown; + static XSolidBrush _royalBlue; + static XSolidBrush _saddleBrown; + static XSolidBrush _salmon; + static XSolidBrush _sandyBrown; + static XSolidBrush _seaGreen; + static XSolidBrush _seaShell; + static XSolidBrush _sienna; + static XSolidBrush _silver; + static XSolidBrush _skyBlue; + static XSolidBrush _slateBlue; + static XSolidBrush _slateGray; + static XSolidBrush _snow; + static XSolidBrush _springGreen; + static XSolidBrush _steelBlue; + static XSolidBrush _tan; + static XSolidBrush _teal; + static XSolidBrush _thistle; + static XSolidBrush _tomato; + static XSolidBrush _transparent; + static XSolidBrush _turquoise; + static XSolidBrush _violet; + static XSolidBrush _wheat; + static XSolidBrush _white; + static XSolidBrush _whiteSmoke; + static XSolidBrush _yellow; + static XSolidBrush _yellowGreen; +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XColor.cs b/src/PDFsharp/src/PdfSharp/Drawing/XColor.cs new file mode 100644 index 00000000..75a29633 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XColor.cs @@ -0,0 +1,824 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.ComponentModel; +#if GDI +using System.Drawing; +#endif +#if WPF +using WpfColor = System.Windows.Media.Color; +#endif +#if UWP +using UwpColor = Windows.UI.Color; +#endif + + +// ReSharper disable RedundantNameQualifier + +namespace PdfSharp.Drawing +{ + /// + /// Represents a RGB, CMYK, or gray scale color. + /// + [DebuggerDisplay("clr=(A={A}, R={R}, G={G}, B={B} C={C}, M={M}, Y={Y}, K={K})")] + public struct XColor + { + XColor(uint argb) + { + _cs = XColorSpace.Rgb; + _a = (byte)((argb >> 24) & 0xff) / 255f; + _r = (byte)((argb >> 16) & 0xff); + _g = (byte)((argb >> 8) & 0xff); + _b = (byte)(argb & 0xff); + _c = 0; + _m = 0; + _y = 0; + _k = 0; + _gs = 0; + RgbChanged(); + //_cs.GetType(); // Suppress warning + } + + XColor(byte alpha, byte red, byte green, byte blue) + { + _cs = XColorSpace.Rgb; + _a = alpha / 255f; + _r = red; + _g = green; + _b = blue; + _c = 0; + _m = 0; + _y = 0; + _k = 0; + _gs = 0; + RgbChanged(); + //_cs.GetType(); // Suppress warning + } + + XColor(double alpha, double cyan, double magenta, double yellow, double black) + { + _cs = XColorSpace.Cmyk; + _a = (float)(alpha > 1 ? 1 : (alpha < 0 ? 0 : alpha)); + _c = (float)(cyan > 1 ? 1 : (cyan < 0 ? 0 : cyan)); + _m = (float)(magenta > 1 ? 1 : (magenta < 0 ? 0 : magenta)); + _y = (float)(yellow > 1 ? 1 : (yellow < 0 ? 0 : yellow)); + _k = (float)(black > 1 ? 1 : (black < 0 ? 0 : black)); + _r = 0; + _g = 0; + _b = 0; + _gs = 0f; + CmykChanged(); + } + + XColor(double cyan, double magenta, double yellow, double black) + : this(1.0, cyan, magenta, yellow, black) + { } + + XColor(double gray) + { + _cs = XColorSpace.GrayScale; + if (gray < 0) + _gs = 0; + else if (gray > 1) + _gs = 1; + else + _gs = (float)gray; + + _a = 1; + _r = 0; + _g = 0; + _b = 0; + _c = 0; + _m = 0; + _y = 0; + _k = 0; + GrayChanged(); + } + +#if GDI + XColor(System.Drawing.Color color) + : this(color.A, color.R, color.G, color.B) + { } +#endif + +#if WPF + XColor(WpfColor color) + : this(color.A, color.R, color.G, color.B) + { } +#endif + +#if GDI + XColor(KnownColor knownColor) + : this(System.Drawing.Color.FromKnownColor(knownColor)) + { } +#endif + +#if UWP + XColor(UwpColor color) + : this(color.A, color.R, color.G, color.B) + { } +#endif + + internal XColor(XKnownColor knownColor) + : this(XKnownColorTable.KnownColorToArgb(knownColor)) + { } + + /// + /// Creates an XColor structure from a 32-bit ARGB value. + /// + public static XColor FromArgb(int argb) + { + return new XColor((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)(argb)); + } + + /// + /// Creates an XColor structure from a 32-bit ARGB value. + /// + public static XColor FromArgb(uint argb) + { + return new XColor((byte)(argb >> 24), (byte)(argb >> 16), (byte)(argb >> 8), (byte)(argb)); + } + + // from System.Drawing.Color + //public static XColor FromArgb(int alpha, Color baseColor); + //public static XColor FromArgb(int red, int green, int blue); + //public static XColor FromArgb(int alpha, int red, int green, int blue); + //public static XColor FromKnownColor(KnownColor color); + //public static XColor FromName(string name); + + /// + /// Creates an XColor structure from the specified 8-bit color values (red, green, and blue). + /// The alpha value is implicitly 255 (fully opaque). + /// + public static XColor FromArgb(int red, int green, int blue) + { + CheckByte(red, "red"); + CheckByte(green, "green"); + CheckByte(blue, "blue"); + return new XColor(255, (byte)red, (byte)green, (byte)blue); + } + + /// + /// Creates an XColor structure from the four ARGB component (alpha, red, green, and blue) values. + /// + public static XColor FromArgb(int alpha, int red, int green, int blue) + { + CheckByte(alpha, "alpha"); + CheckByte(red, "red"); + CheckByte(green, "green"); + CheckByte(blue, "blue"); + return new XColor((byte)alpha, (byte)red, (byte)green, (byte)blue); + } + +#if GDI + /// + /// Creates an XColor structure from the specified System.Drawing.Color. + /// + public static XColor FromArgb(System.Drawing.Color color) + { + return new XColor(color); + } +#endif + +#if WPF + /// + /// Creates an XColor structure from the specified System.Drawing.Color. + /// + public static XColor FromArgb(WpfColor color) + { + return new XColor(color); + } +#endif + +#if UWP + /// + /// Creates an XColor structure from the specified Windows.UI.Color. + /// + public static XColor FromArgb(UwpColor color) + { + return new XColor(color); + } +#endif + + /// + /// Creates an XColor structure from the specified alpha value and color. + /// + public static XColor FromArgb(int alpha, XColor color) + { + color.A = ((byte)alpha) / 255.0; + return color; + } + +#if GDI + /// + /// Creates an XColor structure from the specified alpha value and color. + /// + public static XColor FromArgb(int alpha, System.Drawing.Color color) + { + // Cast required to use correct constructor. + return new XColor((byte)alpha, color.R, color.G, color.B); + } +#endif + +#if WPF + /// + /// Creates an XColor structure from the specified alpha value and color. + /// + public static XColor FromArgb(int alpha, WpfColor color) + { + // Cast required to use correct constructor. + return new XColor((byte)alpha, color.R, color.G, color.B); + } +#endif + +#if UWP + /// + /// Creates an XColor structure from the specified alpha value and color. + /// + public static XColor FromArgb(int alpha, UwpColor color) + { + // Cast required to use correct constructor. + return new XColor((byte)alpha, color.R, color.G, color.B); + } +#endif + + /// + /// Creates an XColor structure from the specified CMYK values. + /// + public static XColor FromCmyk(double cyan, double magenta, double yellow, double black) + { + return new XColor(cyan, magenta, yellow, black); + } + + /// + /// Creates an XColor structure from the specified CMYK values. + /// + public static XColor FromCmyk(double alpha, double cyan, double magenta, double yellow, double black) + { + return new XColor(alpha, cyan, magenta, yellow, black); + } + + /// + /// Creates an XColor structure from the specified gray value. + /// + public static XColor FromGrayScale(double grayScale) + { + return new XColor(grayScale); + } + + /// + /// Creates an XColor from the specified pre-defined color. + /// + public static XColor FromKnownColor(XKnownColor color) + { + return new XColor(color); + } + +#if GDI + /// + /// Creates an XColor from the specified pre-defined color. + /// + public static XColor FromKnownColor(KnownColor color) + { + return new XColor(color); + } +#endif + + /// + /// Creates an XColor from the specified name of a pre-defined color. + /// + public static XColor FromName(string name) + { +#if GDI + // The implementation in System.Drawing.dll is interesting. It uses a ColorConverter + // with hash tables, locking mechanisms etc. I'm not sure what problems that solves. + // So I don't use the source, but the reflection. + try + { + return new XColor((KnownColor)Enum.Parse(typeof(KnownColor), name, true)); + } + // ReSharper disable EmptyGeneralCatchClause + catch { } + // ReSharper restore EmptyGeneralCatchClause +#endif + return Empty; + } + + /// + /// Gets or sets the color space to be used for PDF generation. + /// + public XColorSpace ColorSpace + { + get { return _cs; } + set + { + if (!Enum.IsDefined(typeof(XColorSpace), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(XColorSpace)); + _cs = value; + } + } + + /// + /// Indicates whether this XColor structure is uninitialized. + /// + public bool IsEmpty + { + get { return this == Empty; } + } + +#if GDI +#if UseGdiObjects + /// + /// Implicit conversion from Color to XColor + /// + public static implicit operator XColor(Color color) + { + return new XColor(color); + } +#endif + + /// + /// Creates a System.Drawing.Color object from this color. + /// + public System.Drawing.Color ToGdiColor() + { + return System.Drawing.Color.FromArgb((int)(_a * 255), _r, _g, _b); + } +#endif + +#if WPF + /// + /// Creates a WpfColor object from this color. + /// + public WpfColor ToWpfColor() + { + return WpfColor.FromArgb((byte)(_a * 255), _r, _g, _b); + } +#endif + +#if UWP + /// + /// Creates a Windows.UI.Color object from this color. + /// + public UwpColor ToUwpColor() + { + return UwpColor.FromArgb((byte)(_a * 255), _r, _g, _b); + } +#endif + + /// + /// Determines whether the specified object is a Color structure and is equivalent to this + /// Color structure. + /// + public override bool Equals(object obj) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (obj is XColor) + { + XColor color = (XColor)obj; + if (_r == color._r && _g == color._g && _b == color._b && + _c == color._c && _m == color._m && _y == color._y && _k == color._k && + _gs == color._gs) + { + return _a == color._a; + } + } + return false; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + // ReSharper disable NonReadonlyFieldInGetHashCode + return ((byte)(_a * 255)) ^ _r ^ _g ^ _b; + // ReSharper restore NonReadonlyFieldInGetHashCode + } + + /// + /// Determines whether two colors are equal. + /// + public static bool operator ==(XColor left, XColor right) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (left._r == right._r && left._g == right._g && left._b == right._b && + left._c == right._c && left._m == right._m && left._y == right._y && left._k == right._k && + left._gs == right._gs) + { + return left._a == right._a; + } + return false; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Determines whether two colors are not equal. + /// + public static bool operator !=(XColor left, XColor right) + { + return !(left == right); + } + + /// + /// Gets a value indicating whether this color is a known color. + /// + public bool IsKnownColor + { + get { return XKnownColorTable.IsKnownColor(Argb); } + } + + /// + /// Gets the hue-saturation-brightness (HSB) hue value, in degrees, for this color. + /// + /// The hue, in degrees, of this color. The hue is measured in degrees, ranging from 0 through 360, in HSB color space. + public double GetHue() + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if ((_r == _g) && (_g == _b)) + return 0; + + double value1 = _r / 255.0; + double value2 = _g / 255.0; + double value3 = _b / 255.0; + double value7 = 0; + double value4 = value1; + double value5 = value1; + if (value2 > value4) + value4 = value2; + + if (value3 > value4) + value4 = value3; + + if (value2 < value5) + value5 = value2; + + if (value3 < value5) + value5 = value3; + + double value6 = value4 - value5; + if (value1 == value4) + value7 = (value2 - value3) / value6; + else if (value2 == value4) + value7 = 2f + ((value3 - value1) / value6); + else if (value3 == value4) + value7 = 4f + ((value1 - value2) / value6); + + value7 *= 60; + if (value7 < 0) + value7 += 360; + return value7; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Gets the hue-saturation-brightness (HSB) saturation value for this color. + /// + /// The saturation of this color. The saturation ranges from 0 through 1, where 0 is grayscale and 1 is the most saturated. + public double GetSaturation() + { + // ReSharper disable CompareOfFloatsByEqualityOperator + double value1 = _r / 255.0; + double value2 = _g / 255.0; + double value3 = _b / 255.0; + double value7 = 0; + double value4 = value1; + double value5 = value1; + if (value2 > value4) + value4 = value2; + + if (value3 > value4) + value4 = value3; + + if (value2 < value5) + value5 = value2; + + if (value3 < value5) + value5 = value3; + + if (value4 == value5) + return value7; + + double value6 = (value4 + value5) / 2; + if (value6 <= 0.5) + return (value4 - value5) / (value4 + value5); + return (value4 - value5) / ((2f - value4) - value5); + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Gets the hue-saturation-brightness (HSB) brightness value for this color. + /// + /// The brightness of this color. The brightness ranges from 0 through 1, where 0 represents black and 1 represents white. + public double GetBrightness() + { + double value1 = _r / 255.0; + double value2 = _g / 255.0; + double value3 = _b / 255.0; + double value4 = value1; + double value5 = value1; + if (value2 > value4) + value4 = value2; + + if (value3 > value4) + value4 = value3; + + if (value2 < value5) + value5 = value2; + + if (value3 < value5) + value5 = value3; + + return (value4 + value5) / 2; + } + + /// + /// One of the RGB values changed; recalculate other color representations. + /// + void RgbChanged() + { + // ReSharper disable LocalVariableHidesMember + _cs = XColorSpace.Rgb; + int c = 255 - _r; + int m = 255 - _g; + int y = 255 - _b; + int k = Math.Min(c, Math.Min(m, y)); + if (k == 255) + _c = _m = _y = 0; + else + { + float black = 255f - k; + _c = (c - k) / black; + _m = (m - k) / black; + _y = (y - k) / black; + } + _k = _gs = k / 255f; + // ReSharper restore LocalVariableHidesMember + } + + /// + /// One of the CMYK values changed; recalculate other color representations. + /// + void CmykChanged() + { + _cs = XColorSpace.Cmyk; + float black = _k * 255; + float factor = 255f - black; + _r = (byte)(255 - Math.Min(255f, _c * factor + black)); + _g = (byte)(255 - Math.Min(255f, _m * factor + black)); + _b = (byte)(255 - Math.Min(255f, _y * factor + black)); + _gs = (float)(1 - Math.Min(1.0, 0.3f * _c + 0.59f * _m + 0.11 * _y + _k)); + } + + /// + /// The gray scale value changed; recalculate other color representations. + /// + void GrayChanged() + { + _cs = XColorSpace.GrayScale; + _r = (byte)(_gs * 255); + _g = (byte)(_gs * 255); + _b = (byte)(_gs * 255); + _c = 0; + _m = 0; + _y = 0; + _k = 1 - _gs; + } + + // Properties + + /// + /// Gets or sets the alpha value the specifies the transparency. + /// The value is in the range from 1 (opaque) to 0 (completely transparent). + /// + public double A + { + get { return _a; } + set + { + if (value < 0) + _a = 0; + else if (value > 1) + _a = 1; + else + _a = (float)value; + } + } + + /// + /// Gets or sets the red value. + /// + public byte R + { + get { return _r; } + set { _r = value; RgbChanged(); } + } + + /// + /// Gets or sets the green value. + /// + public byte G + { + get { return _g; } + set { _g = value; RgbChanged(); } + } + + /// + /// Gets or sets the blue value. + /// + public byte B + { + get { return _b; } + set { _b = value; RgbChanged(); } + } + + /// + /// Gets the RGB part value of the color. Internal helper function. + /// + internal uint Rgb + { + get { return ((uint)_r << 16) | ((uint)_g << 8) | _b; } + } + + /// + /// Gets the ARGB part value of the color. Internal helper function. + /// + internal uint Argb + { + get { return ((uint)(_a * 255) << 24) | ((uint)_r << 16) | ((uint)_g << 8) | _b; } + } + + /// + /// Gets or sets the cyan value. + /// + public double C + { + get { return _c; } + set + { + if (value < 0) + _c = 0; + else if (value > 1) + _c = 1; + else + _c = (float)value; + CmykChanged(); + } + } + + /// + /// Gets or sets the magenta value. + /// + public double M + { + get { return _m; } + set + { + if (value < 0) + _m = 0; + else if (value > 1) + _m = 1; + else + _m = (float)value; + CmykChanged(); + } + } + + /// + /// Gets or sets the yellow value. + /// + public double Y + { + get { return _y; } + set + { + if (value < 0) + _y = 0; + else if (value > 1) + _y = 1; + else + _y = (float)value; + CmykChanged(); + } + } + + /// + /// Gets or sets the black (or key) value. + /// + public double K + { + get { return _k; } + set + { + if (value < 0) + _k = 0; + else if (value > 1) + _k = 1; + else + _k = (float)value; + CmykChanged(); + } + } + + /// + /// Gets or sets the gray scale value. + /// + // ReSharper disable InconsistentNaming + public double GS + // ReSharper restore InconsistentNaming + { + get { return _gs; } + set + { + if (value < 0) + _gs = 0; + else if (value > 1) + _gs = 1; + else + _gs = (float)value; + GrayChanged(); + } + } + + /// + /// Represents the null color. + /// + public static XColor Empty; + + /// + /// Special property for XmlSerializer only. + /// + public string RgbCmykG + { + get + { + return String.Format(CultureInfo.InvariantCulture, + "{0};{1};{2};{3};{4};{5};{6};{7};{8}", _r, _g, _b, _c, _m, _y, _k, _gs, _a); + } + set + { + string[] values = value.Split(';'); + _r = byte.Parse(values[0], CultureInfo.InvariantCulture); + _g = byte.Parse(values[1], CultureInfo.InvariantCulture); + _b = byte.Parse(values[2], CultureInfo.InvariantCulture); + _c = float.Parse(values[3], CultureInfo.InvariantCulture); + _m = float.Parse(values[4], CultureInfo.InvariantCulture); + _y = float.Parse(values[5], CultureInfo.InvariantCulture); + _k = float.Parse(values[6], CultureInfo.InvariantCulture); + _gs = float.Parse(values[7], CultureInfo.InvariantCulture); + _a = float.Parse(values[8], CultureInfo.InvariantCulture); + } + } + + static void CheckByte(int val, string name) + { + if (val < 0 || val > 0xFF) + throw new ArgumentException(PSSR.InvalidValue(val, name, 0, 255)); + } + + XColorSpace _cs; + + float _a; // alpha + + byte _r; // \ + byte _g; // |--- RGB + byte _b; // / + + float _c; // \ + float _m; // |--- CMYK + float _y; // | + float _k; // / + + float _gs; // >--- gray scale + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XColorResourceManager.cs b/src/PDFsharp/src/PdfSharp/Drawing/XColorResourceManager.cs new file mode 100644 index 00000000..68d30152 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XColorResourceManager.cs @@ -0,0 +1,361 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using System.ComponentModel; +using System.Threading; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Manages the localization of the color class. + /// + public class XColorResourceManager + { + /// + /// Initializes a new instance of the class. + /// + public XColorResourceManager() +#if !NETFX_CORE && !UWP + : this(Thread.CurrentThread.CurrentUICulture) +#else + : this(CultureInfo.CurrentUICulture) +#endif + { } + + /// + /// Initializes a new instance of the class. + /// + /// The culture info. + public XColorResourceManager(CultureInfo cultureInfo) + { + _cultureInfo = cultureInfo; + } + + readonly CultureInfo _cultureInfo; + +#if DEBUG_ + static public void Test() + { + int kcc = XKnownColorTable.colorTable.Length; + + for (int idx = 0; idx < kcc; idx++) + { + uint argb = XKnownColorTable.colorTable[idx]; + ColorResourceInfo info = GetColorInfo((XKnownColor)idx); + if ((int)info.KnownColor == -1) + { + kcc.GetType(); + } + else + { + if (argb != info.Argb) + { + kcc.GetType(); + } + } + } + + for (int idx = 0; idx < colorInfos.Length; idx++) + { + ColorResourceInfo c2 = colorInfos[idx]; + if (c2.Argb != c2.Color.Rgb) + c2.GetType(); + } + } +#endif + + /// + /// Gets a known color from an ARGB value. Throws an ArgumentException if the value is not a known color. + /// + public static XKnownColor GetKnownColor(uint argb) + { + XKnownColor knownColor = XKnownColorTable.GetKnownColor(argb); + if ((int)knownColor == -1) + throw new ArgumentException("The argument is not a known color", "argb"); + return knownColor; + } + + /// + /// Gets all known colors. + /// + /// Indicates whether to include the color Transparent. + public static XKnownColor[] GetKnownColors(bool includeTransparent) + { + int count = colorInfos.Length; + XKnownColor[] knownColor = new XKnownColor[count - (includeTransparent ? 0 : 1)]; + for (int idxIn = includeTransparent ? 0 : 1, idxOut = 0; idxIn < count; idxIn++, idxOut++) + knownColor[idxOut] = colorInfos[idxIn].KnownColor; + return knownColor; + } + + /// + /// Converts a known color to a localized color name. + /// + public string ToColorName(XKnownColor knownColor) + { + ColorResourceInfo colorInfo = GetColorInfo(knownColor); + + // Currently German only + if (_cultureInfo.TwoLetterISOLanguageName == "de") + return colorInfo.NameDE; + + return colorInfo.Name; + } + + /// + /// Converts a color to a localized color name or an ARGB value. + /// + public string ToColorName(XColor color) + { + string name; + if (color.IsKnownColor) + name = ToColorName(XKnownColorTable.GetKnownColor(color.Argb)); + else + name = String.Format("{0}, {1}, {2}, {3}", (int)(255 * color.A), color.R, color.G, color.B); + return name; + } + + static ColorResourceInfo GetColorInfo(XKnownColor knownColor) + { + for (int idx = 0; idx < colorInfos.Length; idx++) + { + ColorResourceInfo colorInfo = colorInfos[idx]; + if (colorInfo.KnownColor == knownColor) + return colorInfo; + } + throw new InvalidEnumArgumentException("Enum is not an XKnownColor."); + } + + // I found no official translation for the 140 pre-defined colors. Some folks made their own translations. + // http://unnecessary.de/wuest/farbtab/farbtabelle-w.html + // http://blog.patrickkempf.de/archives/2004/04/10/html-farben/ + // http://www.grafikwunder.de/Grafikecke/Farbtabelle/farbtabelle-006.php + // Silke changed some German translations (women know more colors than men :-) + internal static ColorResourceInfo[] colorInfos = new ColorResourceInfo[] + { + new ColorResourceInfo(XKnownColor.Transparent, XColors.Transparent, 0x00FFFFFF, "Transparent", "Transparent"), + new ColorResourceInfo(XKnownColor.Black, XColors.Black, 0xFF000000, "Black", "Schwarz"), + new ColorResourceInfo(XKnownColor.DarkSlateGray, XColors.DarkSlateGray, 0xFF8FBC8F, "Darkslategray", "Dunkles Schiefergrau"), + new ColorResourceInfo(XKnownColor.SlateGray, XColors.SlateGray, 0xFF708090, "Slategray", "Schiefergrau"), + new ColorResourceInfo(XKnownColor.LightSlateGray, XColors.LightSlateGray, 0xFF778899, "Lightslategray", "Helles Schiefergrau"), + new ColorResourceInfo(XKnownColor.LightSteelBlue, XColors.LightSteelBlue, 0xFFB0C4DE, "Lightsteelblue", "Helles Stahlblau"), + //new ColorResourceInfo(XKnownColor.DimGray, XColors.DimGray, 0xFF696969, "Dimgray", "Mattes Grau"), + new ColorResourceInfo(XKnownColor.DimGray, XColors.DimGray, 0xFF696969, "Dimgray", "Gedecktes Grau"), + new ColorResourceInfo(XKnownColor.Gray, XColors.Gray, 0xFF808080, "Gray", "Grau"), + new ColorResourceInfo(XKnownColor.DarkGray, XColors.DarkGray, 0xFFA9A9A9, "Darkgray", "Dunkelgrau"), + new ColorResourceInfo(XKnownColor.Silver, XColors.Silver, 0xFFC0C0C0, "Silver", "Silber"), + //new ColorResourceInfo(XKnownColor.Gainsboro, XColors.Gainsboro, 0xFFDCDCDC, "Gainsboro", "Gainsboro"), + new ColorResourceInfo(XKnownColor.Gainsboro, XColors.Gainsboro, 0xFFDCDCDC, "Gainsboro", "Helles Blaugrau"), + //new ColorResourceInfo(XKnownColor.WhiteSmoke, XColors.WhiteSmoke, 0xFFF5F5F5, "Whitesmoke", "Rauchiges Weiß"), + new ColorResourceInfo(XKnownColor.WhiteSmoke, XColors.WhiteSmoke, 0xFFF5F5F5, "Whitesmoke", "Rauchweiß"), + //new ColorResourceInfo(XKnownColor.GhostWhite, XColors.GhostWhite, 0xFFF8F8FF, "Ghostwhite", "Geisterweiß"), + new ColorResourceInfo(XKnownColor.GhostWhite, XColors.GhostWhite, 0xFFF8F8FF, "Ghostwhite", "Schattenweiß"), + new ColorResourceInfo(XKnownColor.White, XColors.White, 0xFFFFFFFF, "White", "Weiß"), + new ColorResourceInfo(XKnownColor.Snow, XColors.Snow, 0xFFFFFAFA, "Snow", "Schneeweiß"), + new ColorResourceInfo(XKnownColor.Ivory, XColors.Ivory, 0xFFFFFFF0, "Ivory", "Elfenbein"), + new ColorResourceInfo(XKnownColor.FloralWhite, XColors.FloralWhite, 0xFFFFFAF0, "Floralwhite", "Blütenweiß"), + new ColorResourceInfo(XKnownColor.SeaShell, XColors.SeaShell, 0xFFFFF5EE, "Seashell", "Muschel"), + //new ColorResourceInfo(XKnownColor.OldLace, XColors.OldLace, 0xFFFDF5E6, "Oldlace", "Altgold"), + new ColorResourceInfo(XKnownColor.OldLace, XColors.OldLace, 0xFFFDF5E6, "Oldlace", "Altweiß"), + //new ColorResourceInfo(XKnownColor.Linen, XColors.Linen, 0xFFFAF0E6, "Linen", "Leinenfarbe"), + new ColorResourceInfo(XKnownColor.Linen, XColors.Linen, 0xFFFAF0E6, "Linen", "Leinen"), + new ColorResourceInfo(XKnownColor.AntiqueWhite, XColors.AntiqueWhite, 0xFFFAEBD7, "Antiquewhite", "Antikes Weiß"), + new ColorResourceInfo(XKnownColor.BlanchedAlmond, XColors.BlanchedAlmond, 0xFFFFEBCD, "Blanchedalmond", "Mandelweiß"), + //new ColorResourceInfo(XKnownColor.PapayaWhip, XColors.PapayaWhip, 0xFFFFEFD5, "Papayawhip", "Cremiges Papaya"), + new ColorResourceInfo(XKnownColor.PapayaWhip, XColors.PapayaWhip, 0xFFFFEFD5, "Papayawhip", "Papayacreme"), + new ColorResourceInfo(XKnownColor.Beige, XColors.Beige, 0xFFF5F5DC, "Beige", "Beige"), + new ColorResourceInfo(XKnownColor.Cornsilk, XColors.Cornsilk, 0xFFFFF8DC, "Cornsilk", "Mais"), + //new ColorResourceInfo(XKnownColor.LightGoldenrodYellow, XColors.LightGoldenrodYellow, 0xFFFAFAD2, "Lightgoldenrodyellow", "Helles Goldrutengelb"), + new ColorResourceInfo(XKnownColor.LightGoldenrodYellow, XColors.LightGoldenrodYellow, 0xFFFAFAD2, "Lightgoldenrodyellow", "Helles Goldgelb"), + new ColorResourceInfo(XKnownColor.LightYellow, XColors.LightYellow, 0xFFFFFFE0, "Lightyellow", "Hellgelb"), + new ColorResourceInfo(XKnownColor.LemonChiffon, XColors.LemonChiffon, 0xFFFFFACD, "Lemonchiffon", "Pastellgelb"), + //new ColorResourceInfo(XKnownColor.PaleGoldenrod, XColors.PaleGoldenrod, 0xFFEEE8AA, "Palegoldenrod", "Blasse Goldrutenfarbe"), + new ColorResourceInfo(XKnownColor.PaleGoldenrod, XColors.PaleGoldenrod, 0xFFEEE8AA, "Palegoldenrod", "Blasses Goldgelb"), + new ColorResourceInfo(XKnownColor.Khaki, XColors.Khaki, 0xFFF0E68C, "Khaki", "Khaki"), + new ColorResourceInfo(XKnownColor.Yellow, XColors.Yellow, 0xFFFFFF00, "Yellow", "Gelb"), + new ColorResourceInfo(XKnownColor.Gold, XColors.Gold, 0xFFFFD700, "Gold", "Gold"), + new ColorResourceInfo(XKnownColor.Orange, XColors.Orange, 0xFFFFA500, "Orange", "Orange"), + new ColorResourceInfo(XKnownColor.DarkOrange, XColors.DarkOrange, 0xFFFF8C00, "Darkorange", "Dunkles Orange"), + //new ColorResourceInfo(XKnownColor.Goldenrod, XColors.Goldenrod, 0xFFDAA520, "Goldenrod", "Goldrute"), + new ColorResourceInfo(XKnownColor.Goldenrod, XColors.Goldenrod, 0xFFDAA520, "Goldenrod", "Goldgelb"), + //new ColorResourceInfo(XKnownColor.DarkGoldenrod, XColors.DarkGoldenrod, 0xFFB8860B, "Darkgoldenrod", "Dunkle Goldrutenfarbe"), + new ColorResourceInfo(XKnownColor.DarkGoldenrod, XColors.DarkGoldenrod, 0xFFB8860B, "Darkgoldenrod", "Dunkles Goldgelb"), + new ColorResourceInfo(XKnownColor.Peru, XColors.Peru, 0xFFCD853F, "Peru", "Peru"), + new ColorResourceInfo(XKnownColor.Chocolate, XColors.Chocolate, 0xFFD2691E, "Chocolate", "Schokolade"), + new ColorResourceInfo(XKnownColor.SaddleBrown, XColors.SaddleBrown, 0xFF8B4513, "Saddlebrown", "Sattelbraun"), + new ColorResourceInfo(XKnownColor.Sienna, XColors.Sienna, 0xFFA0522D, "Sienna", "Ocker"), + new ColorResourceInfo(XKnownColor.Brown, XColors.Brown, 0xFFA52A2A, "Brown", "Braun"), + new ColorResourceInfo(XKnownColor.DarkRed, XColors.DarkRed, 0xFF8B0000, "Darkred", "Dunkelrot"), + new ColorResourceInfo(XKnownColor.Maroon, XColors.Maroon, 0xFF800000, "Maroon", "Kastanienbraun"), + new ColorResourceInfo(XKnownColor.PaleTurquoise, XColors.PaleTurquoise, 0xFFAFEEEE, "Paleturquoise", "Blasses Türkis"), + //new ColorResourceInfo(XKnownColor.Firebrick, XColors.Firebrick, 0xFFB22222, "Firebrick", "Ziegelfarbe"), + new ColorResourceInfo(XKnownColor.Firebrick, XColors.Firebrick, 0xFFB22222, "Firebrick", "Ziegel"), + new ColorResourceInfo(XKnownColor.IndianRed, XColors.IndianRed, 0xFFCD5C5C, "Indianred", "Indischrot"), + new ColorResourceInfo(XKnownColor.Crimson, XColors.Crimson, 0xFFDC143C, "Crimson", "Karmesinrot"), + new ColorResourceInfo(XKnownColor.Red, XColors.Red, 0xFFFF0000, "Red", "Rot"), + //new ColorResourceInfo(XKnownColor.OrangeRed, XColors.OrangeRed, 0xFFFF4500, "Orangered", "Orangenrot"), + new ColorResourceInfo(XKnownColor.OrangeRed, XColors.OrangeRed, 0xFFFF4500, "Orangered", "Orangerot"), + //new ColorResourceInfo(XKnownColor.Tomato, XColors.Tomato, 0xFFFF6347, "Tomato", "Tomatenrot"), + new ColorResourceInfo(XKnownColor.Tomato, XColors.Tomato, 0xFFFF6347, "Tomato", "Tomate"), + new ColorResourceInfo(XKnownColor.Coral, XColors.Coral, 0xFFFF7F50, "Coral", "Koralle"), + new ColorResourceInfo(XKnownColor.Salmon, XColors.Salmon, 0xFFFA8072, "Salmon", "Lachs"), + new ColorResourceInfo(XKnownColor.LightCoral, XColors.LightCoral, 0xFFF08080, "Lightcoral", "Helles Korallenrot"), + //new ColorResourceInfo(XKnownColor.DarkSalmon, XColors.DarkSalmon, 0xFFE9967A, "Darksalmon", "Dunkle Lachsfarbe"), + new ColorResourceInfo(XKnownColor.DarkSalmon, XColors.DarkSalmon, 0xFFE9967A, "Darksalmon", "Dunkles Lachs"), + //new ColorResourceInfo(XKnownColor.LightSalmon, XColors.LightSalmon, 0xFFFFA07A, "Lightsalmon", "Helle Lachsfarbe"), + new ColorResourceInfo(XKnownColor.LightSalmon, XColors.LightSalmon, 0xFFFFA07A, "Lightsalmon", "Helles Lachs"), + new ColorResourceInfo(XKnownColor.SandyBrown, XColors.SandyBrown, 0xFFF4A460, "Sandybrown", "Sandbraun"), + //new ColorResourceInfo(XKnownColor.RosyBrown, XColors.RosyBrown, 0xFFBC8F8F, "Rosybrown", "Rosiges Braun"), + new ColorResourceInfo(XKnownColor.RosyBrown, XColors.RosyBrown, 0xFFBC8F8F, "Rosybrown", "Rotbraun"), + new ColorResourceInfo(XKnownColor.Tan, XColors.Tan, 0xFFD2B48C, "Tan", "Gelbbraun"), + //new ColorResourceInfo(XKnownColor.BurlyWood, XColors.BurlyWood, 0xFFDEB887, "Burlywood", "Grobes Braun"), + new ColorResourceInfo(XKnownColor.BurlyWood, XColors.BurlyWood, 0xFFDEB887, "Burlywood", "Kräftiges Sandbraun"), + new ColorResourceInfo(XKnownColor.Wheat, XColors.Wheat, 0xFFF5DEB3, "Wheat", "Weizen"), + new ColorResourceInfo(XKnownColor.PeachPuff, XColors.PeachPuff, 0xFFFFDAB9, "Peachpuff", "Pfirsich"), + //new ColorResourceInfo(XKnownColor.NavajoWhite, XColors.NavajoWhite, 0xFFFFDEAD, "Navajowhite", "Navajoweiß"), + new ColorResourceInfo(XKnownColor.NavajoWhite, XColors.NavajoWhite, 0xFFFFDEAD, "Navajowhite", "Orangeweiß"), + //new ColorResourceInfo(XKnownColor.Bisque, XColors.Bisque, 0xFFFFE4C4, "Bisque", "Tomatencreme"), + new ColorResourceInfo(XKnownColor.Bisque, XColors.Bisque, 0xFFFFE4C4, "Bisque", "Blasses Rotbraun"), + //new ColorResourceInfo(XKnownColor.Moccasin, XColors.Moccasin, 0xFFFFE4B5, "Moccasin", "Moccasin"), + new ColorResourceInfo(XKnownColor.Moccasin, XColors.Moccasin, 0xFFFFE4B5, "Moccasin", "Mokassin"), + //new ColorResourceInfo(XKnownColor.LavenderBlush, XColors.LavenderBlush, 0xFFFFF0F5, "Lavenderblush", "Rosige Lavenderfarbe"), + new ColorResourceInfo(XKnownColor.LavenderBlush, XColors.LavenderBlush, 0xFFFFF0F5, "Lavenderblush", "Roter Lavendel"), + new ColorResourceInfo(XKnownColor.MistyRose, XColors.MistyRose, 0xFFFFE4E1, "Mistyrose", "Altrosa"), + new ColorResourceInfo(XKnownColor.Pink, XColors.Pink, 0xFFFFC0CB, "Pink", "Rosa"), + new ColorResourceInfo(XKnownColor.LightPink, XColors.LightPink, 0xFFFFB6C1, "Lightpink", "Hellrosa"), + new ColorResourceInfo(XKnownColor.HotPink, XColors.HotPink, 0xFFFF69B4, "Hotpink", "Leuchtendes Rosa"), + //// XKnownColor.Fuchsia removed because the same as XKnownColor.Magenta + ////new ColorResourceInfo(XKnownColor.Fuchsia, XColors.Fuchsia, 0xFFFF00FF, "Fuchsia", "Fuchsie"), + new ColorResourceInfo(XKnownColor.Magenta, XColors.Magenta, 0xFFFF00FF, "Magenta", "Magentarot"), + new ColorResourceInfo(XKnownColor.DeepPink, XColors.DeepPink, 0xFFFF1493, "Deeppink", "Tiefrosa"), + new ColorResourceInfo(XKnownColor.MediumVioletRed, XColors.MediumVioletRed, 0xFFC71585, "Mediumvioletred", "Mittleres Violettrot"), + new ColorResourceInfo(XKnownColor.PaleVioletRed, XColors.PaleVioletRed, 0xFFDB7093, "Palevioletred", "Blasses Violettrot"), + new ColorResourceInfo(XKnownColor.Plum, XColors.Plum, 0xFFDDA0DD, "Plum", "Pflaume"), + new ColorResourceInfo(XKnownColor.Thistle, XColors.Thistle, 0xFFD8BFD8, "Thistle", "Distel"), + //new ColorResourceInfo(XKnownColor.Lavender, XColors.Lavender, 0xFFE6E6FA, "Lavender", "Lavendelfarbe"), + new ColorResourceInfo(XKnownColor.Lavender, XColors.Lavender, 0xFFE6E6FA, "Lavender", "Lavendel"), + new ColorResourceInfo(XKnownColor.Violet, XColors.Violet, 0xFFEE82EE, "Violet", "Violett"), + new ColorResourceInfo(XKnownColor.Orchid, XColors.Orchid, 0xFFDA70D6, "Orchid", "Orchidee"), + new ColorResourceInfo(XKnownColor.DarkMagenta, XColors.DarkMagenta, 0xFF8B008B, "Darkmagenta", "Dunkles Magentarot"), + new ColorResourceInfo(XKnownColor.Purple, XColors.Purple, 0xFF800080, "Purple", "Violett"), + new ColorResourceInfo(XKnownColor.Indigo, XColors.Indigo, 0xFF4B0082, "Indigo", "Indigo"), + new ColorResourceInfo(XKnownColor.BlueViolet, XColors.BlueViolet, 0xFF8A2BE2, "Blueviolet", "Blauviolett"), + new ColorResourceInfo(XKnownColor.DarkViolet, XColors.DarkViolet, 0xFF9400D3, "Darkviolet", "Dunkles Violett"), + //new ColorResourceInfo(XKnownColor.DarkOrchid, XColors.DarkOrchid, 0xFF9932CC, "Darkorchid", "Dunkle Orchideenfarbe"), + new ColorResourceInfo(XKnownColor.DarkOrchid, XColors.DarkOrchid, 0xFF9932CC, "Darkorchid", "Dunkle Orchidee"), + new ColorResourceInfo(XKnownColor.MediumPurple, XColors.MediumPurple, 0xFF9370DB, "Mediumpurple", "Mittleres Violett"), + //new ColorResourceInfo(XKnownColor.MediumOrchid, XColors.MediumOrchid, 0xFFBA55D3, "Mediumorchid", "Mittlere Orchideenfarbe"), + new ColorResourceInfo(XKnownColor.MediumOrchid, XColors.MediumOrchid, 0xFFBA55D3, "Mediumorchid", "Mittlere Orchidee"), + new ColorResourceInfo(XKnownColor.MediumSlateBlue, XColors.MediumSlateBlue, 0xFF7B68EE, "Mediumslateblue", "Mittleres Schieferblau"), + new ColorResourceInfo(XKnownColor.SlateBlue, XColors.SlateBlue, 0xFF6A5ACD, "Slateblue", "Schieferblau"), + new ColorResourceInfo(XKnownColor.DarkSlateBlue, XColors.DarkSlateBlue, 0xFF483D8B, "Darkslateblue", "Dunkles Schiefergrau"), + new ColorResourceInfo(XKnownColor.MidnightBlue, XColors.MidnightBlue, 0xFF191970, "Midnightblue", "Mitternachtsblau"), + new ColorResourceInfo(XKnownColor.Navy, XColors.Navy, 0xFF000080, "Navy", "Marineblau"), + new ColorResourceInfo(XKnownColor.DarkBlue, XColors.DarkBlue, 0xFF00008B, "Darkblue", "Dunkelblau"), + new ColorResourceInfo(XKnownColor.LightGray, XColors.LightGray, 0xFFD3D3D3, "Lightgray", "Hellgrau"), + new ColorResourceInfo(XKnownColor.MediumBlue, XColors.MediumBlue, 0xFF0000CD, "Mediumblue", "Mittelblau"), + new ColorResourceInfo(XKnownColor.Blue, XColors.Blue, 0xFF0000FF, "Blue", "Blau"), + new ColorResourceInfo(XKnownColor.RoyalBlue, XColors.RoyalBlue, 0xFF4169E1, "Royalblue", "Königsblau"), + new ColorResourceInfo(XKnownColor.SteelBlue, XColors.SteelBlue, 0xFF4682B4, "Steelblue", "Stahlblau"), + new ColorResourceInfo(XKnownColor.CornflowerBlue, XColors.CornflowerBlue, 0xFF6495ED, "Cornflowerblue", "Kornblumenblau"), + new ColorResourceInfo(XKnownColor.DodgerBlue, XColors.DodgerBlue, 0xFF1E90FF, "Dodgerblue", "Dodger-Blau"), + new ColorResourceInfo(XKnownColor.DeepSkyBlue, XColors.DeepSkyBlue, 0xFF00BFFF, "Deepskyblue", "Tiefes Himmelblau"), + new ColorResourceInfo(XKnownColor.LightSkyBlue, XColors.LightSkyBlue, 0xFF87CEFA, "Lightskyblue", "Helles Himmelblau"), + new ColorResourceInfo(XKnownColor.SkyBlue, XColors.SkyBlue, 0xFF87CEEB, "Skyblue", "Himmelblau"), + new ColorResourceInfo(XKnownColor.LightBlue, XColors.LightBlue, 0xFFADD8E6, "Lightblue", "Hellblau"), + //// XKnownColor.Aqua removed because the same as XKnownColor.Cyan + ////new ColorResourceInfo(XKnownColor.Aqua, XColors.Aqua, 0xFF00FFFF, "Aqua", "Blaugrün"), + new ColorResourceInfo(XKnownColor.Cyan, XColors.Cyan, 0xFF00FFFF, "Cyan", "Zyan"), + new ColorResourceInfo(XKnownColor.PowderBlue, XColors.PowderBlue, 0xFFB0E0E6, "Powderblue", "Taubenblau"), + new ColorResourceInfo(XKnownColor.LightCyan, XColors.LightCyan, 0xFFE0FFFF, "Lightcyan", "Helles Cyanblau"), + new ColorResourceInfo(XKnownColor.AliceBlue, XColors.AliceBlue, 0xFFA0CE00, "Aliceblue", "Aliceblau"), + new ColorResourceInfo(XKnownColor.Azure, XColors.Azure, 0xFFF0FFFF, "Azure", "Himmelblau"), + //new ColorResourceInfo(XKnownColor.MintCream, XColors.MintCream, 0xFFF5FFFA, "Mintcream", "Cremige Pfefferminzfarbe"), + new ColorResourceInfo(XKnownColor.MintCream, XColors.MintCream, 0xFFF5FFFA, "Mintcream", "Helles Pfefferminzgrün"), + new ColorResourceInfo(XKnownColor.Honeydew, XColors.Honeydew, 0xFFF0FFF0, "Honeydew", "Honigmelone"), + new ColorResourceInfo(XKnownColor.Aquamarine, XColors.Aquamarine, 0xFF7FFFD4, "Aquamarine", "Aquamarinblau"), + new ColorResourceInfo(XKnownColor.Turquoise, XColors.Turquoise, 0xFF40E0D0, "Turquoise", "Türkis"), + new ColorResourceInfo(XKnownColor.MediumTurquoise, XColors.MediumTurquoise, 0xFF48D1CC, "Mediumturqoise", "Mittleres Türkis"), + new ColorResourceInfo(XKnownColor.DarkTurquoise, XColors.DarkTurquoise, 0xFF00CED1, "Darkturquoise", "Dunkles Türkis"), + new ColorResourceInfo(XKnownColor.MediumAquamarine, XColors.MediumAquamarine, 0xFF66CDAA, "Mediumaquamarine", "Mittleres Aquamarinblau"), + new ColorResourceInfo(XKnownColor.LightSeaGreen, XColors.LightSeaGreen, 0xFF20B2AA, "Lightseagreen", "Helles Seegrün"), + new ColorResourceInfo(XKnownColor.DarkCyan, XColors.DarkCyan, 0xFF008B8B, "Darkcyan", "Dunkles Zyanblau"), + //new ColorResourceInfo(XKnownColor.Teal, XColors.Teal, 0xFF008080, "Teal", "Entenbraun"), + new ColorResourceInfo(XKnownColor.Teal, XColors.Teal, 0xFF008080, "Teal", "Entenblau"), + new ColorResourceInfo(XKnownColor.CadetBlue, XColors.CadetBlue, 0xFF5F9EA0, "Cadetblue", "Kadettblau"), + new ColorResourceInfo(XKnownColor.MediumSeaGreen, XColors.MediumSeaGreen, 0xFF3CB371, "Mediumseagreen", "Mittleres Seegrün"), + new ColorResourceInfo(XKnownColor.DarkSeaGreen, XColors.DarkSeaGreen, 0xFF8FBC8F, "Darkseagreen", "Dunkles Seegrün"), + new ColorResourceInfo(XKnownColor.LightGreen, XColors.LightGreen, 0xFF90EE90, "Lightgreen", "Hellgrün"), + new ColorResourceInfo(XKnownColor.PaleGreen, XColors.PaleGreen, 0xFF98FB98, "Palegreen", "Blassgrün"), + new ColorResourceInfo(XKnownColor.MediumSpringGreen, XColors.MediumSpringGreen, 0xFF00FA9A, "Mediumspringgreen", "Mittleres Frühlingsgrün"), + new ColorResourceInfo(XKnownColor.SpringGreen, XColors.SpringGreen, 0xFF00FF7F, "Springgreen", "Frühlingsgrün"), + new ColorResourceInfo(XKnownColor.Lime, XColors.Lime, 0xFF00FF00, "Lime", "Zitronengrün"), + new ColorResourceInfo(XKnownColor.LimeGreen, XColors.LimeGreen, 0xFF32CD32, "Limegreen", "Gelbgrün"), + new ColorResourceInfo(XKnownColor.SeaGreen, XColors.SeaGreen, 0xFF2E8B57, "Seagreen", "Seegrün"), + new ColorResourceInfo(XKnownColor.ForestGreen, XColors.ForestGreen, 0xFF228B22, "Forestgreen", "Waldgrün"), + new ColorResourceInfo(XKnownColor.Green, XColors.Green, 0xFF008000, "Green", "Grün"), + new ColorResourceInfo(XKnownColor.LawnGreen, XColors.LawnGreen, 0xFF008000, "LawnGreen", "Grasgrün"), + new ColorResourceInfo(XKnownColor.DarkGreen, XColors.DarkGreen, 0xFF006400, "Darkgreen", "Dunkelgrün"), + //new ColorResourceInfo(XKnownColor.OliveDrab, XColors.OliveDrab, 0xFF6B8E23, "Olivedrab", "Olivfarbiges Graubraun"), + new ColorResourceInfo(XKnownColor.OliveDrab, XColors.OliveDrab, 0xFF6B8E23, "Olivedrab", "Reife Olive"), + new ColorResourceInfo(XKnownColor.DarkOliveGreen, XColors.DarkOliveGreen, 0xFF556B2F, "Darkolivegreen", "Dunkles Olivgrün"), + new ColorResourceInfo(XKnownColor.Olive, XColors.Olive, 0xFF808000, "Olive", "Olivgrün"), + new ColorResourceInfo(XKnownColor.DarkKhaki, XColors.DarkKhaki, 0xFFBDB76B, "Darkkhaki", "Dunkles Khaki"), + new ColorResourceInfo(XKnownColor.YellowGreen, XColors.YellowGreen, 0xFF9ACD32, "Yellowgreen", "Gelbgrün"), + new ColorResourceInfo(XKnownColor.Chartreuse, XColors.Chartreuse, 0xFF7FFF00, "Chartreuse", "Hellgrün"), + new ColorResourceInfo(XKnownColor.GreenYellow, XColors.GreenYellow, 0xFFADFF2F, "Greenyellow", "Grüngelb"), + }; + + internal struct ColorResourceInfo + { + public ColorResourceInfo(XKnownColor knownColor, XColor color, uint argb, string name, string nameDE) + { + KnownColor = knownColor; + Color = color; + Argb = argb; + Name = name; + NameDE = nameDE; + } + public XKnownColor KnownColor; + public XColor Color; + public uint Argb; + public string Name; + // ReSharper disable once InconsistentNaming + public string NameDE; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XColors.cs b/src/PDFsharp/src/PdfSharp/Drawing/XColors.cs new file mode 100644 index 00000000..86b26d79 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XColors.cs @@ -0,0 +1,468 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents a set of 141 pre-defined RGB colors. Incidentally the values are the same + /// as in System.Drawing.Color. + /// + public static class XColors + { + ///Gets a predefined color. + public static XColor AliceBlue { get { return new XColor(XKnownColor.AliceBlue); } } + + ///Gets a predefined color. + public static XColor AntiqueWhite { get { return new XColor(XKnownColor.AntiqueWhite); } } + + ///Gets a predefined color. + public static XColor Aqua { get { return new XColor(XKnownColor.Aqua); } } + + ///Gets a predefined color. + public static XColor Aquamarine { get { return new XColor(XKnownColor.Aquamarine); } } + + ///Gets a predefined color. + public static XColor Azure { get { return new XColor(XKnownColor.Azure); } } + + ///Gets a predefined color. + public static XColor Beige { get { return new XColor(XKnownColor.Beige); } } + + ///Gets a predefined color. + public static XColor Bisque { get { return new XColor(XKnownColor.Bisque); } } + + ///Gets a predefined color. + public static XColor Black { get { return new XColor(XKnownColor.Black); } } + + ///Gets a predefined color. + public static XColor BlanchedAlmond { get { return new XColor(XKnownColor.BlanchedAlmond); } } + + ///Gets a predefined color. + public static XColor Blue { get { return new XColor(XKnownColor.Blue); } } + + ///Gets a predefined color. + public static XColor BlueViolet { get { return new XColor(XKnownColor.BlueViolet); } } + + ///Gets a predefined color. + public static XColor Brown { get { return new XColor(XKnownColor.Brown); } } + + ///Gets a predefined color. + public static XColor BurlyWood { get { return new XColor(XKnownColor.BurlyWood); } } + + ///Gets a predefined color. + public static XColor CadetBlue { get { return new XColor(XKnownColor.CadetBlue); } } + + ///Gets a predefined color. + public static XColor Chartreuse { get { return new XColor(XKnownColor.Chartreuse); } } + + ///Gets a predefined color. + public static XColor Chocolate { get { return new XColor(XKnownColor.Chocolate); } } + + ///Gets a predefined color. + public static XColor Coral { get { return new XColor(XKnownColor.Coral); } } + + ///Gets a predefined color. + public static XColor CornflowerBlue { get { return new XColor(XKnownColor.CornflowerBlue); } } + + ///Gets a predefined color. + public static XColor Cornsilk { get { return new XColor(XKnownColor.Cornsilk); } } + + ///Gets a predefined color. + public static XColor Crimson { get { return new XColor(XKnownColor.Crimson); } } + + ///Gets a predefined color. + public static XColor Cyan { get { return new XColor(XKnownColor.Cyan); } } + + ///Gets a predefined color. + public static XColor DarkBlue { get { return new XColor(XKnownColor.DarkBlue); } } + + ///Gets a predefined color. + public static XColor DarkCyan { get { return new XColor(XKnownColor.DarkCyan); } } + + ///Gets a predefined color. + public static XColor DarkGoldenrod { get { return new XColor(XKnownColor.DarkGoldenrod); } } + + ///Gets a predefined color. + public static XColor DarkGray { get { return new XColor(XKnownColor.DarkGray); } } + + ///Gets a predefined color. + public static XColor DarkGreen { get { return new XColor(XKnownColor.DarkGreen); } } + + ///Gets a predefined color. + public static XColor DarkKhaki { get { return new XColor(XKnownColor.DarkKhaki); } } + + ///Gets a predefined color. + public static XColor DarkMagenta { get { return new XColor(XKnownColor.DarkMagenta); } } + + ///Gets a predefined color. + public static XColor DarkOliveGreen { get { return new XColor(XKnownColor.DarkOliveGreen); } } + + ///Gets a predefined color. + public static XColor DarkOrange { get { return new XColor(XKnownColor.DarkOrange); } } + + ///Gets a predefined color. + public static XColor DarkOrchid { get { return new XColor(XKnownColor.DarkOrchid); } } + + ///Gets a predefined color. + public static XColor DarkRed { get { return new XColor(XKnownColor.DarkRed); } } + + ///Gets a predefined color. + public static XColor DarkSalmon { get { return new XColor(XKnownColor.DarkSalmon); } } + + ///Gets a predefined color. + public static XColor DarkSeaGreen { get { return new XColor(XKnownColor.DarkSeaGreen); } } + + ///Gets a predefined color. + public static XColor DarkSlateBlue { get { return new XColor(XKnownColor.DarkSlateBlue); } } + + ///Gets a predefined color. + public static XColor DarkSlateGray { get { return new XColor(XKnownColor.DarkSlateGray); } } + + ///Gets a predefined color. + public static XColor DarkTurquoise { get { return new XColor(XKnownColor.DarkTurquoise); } } + + ///Gets a predefined color. + public static XColor DarkViolet { get { return new XColor(XKnownColor.DarkViolet); } } + + ///Gets a predefined color. + public static XColor DeepPink { get { return new XColor(XKnownColor.DeepPink); } } + + ///Gets a predefined color. + public static XColor DeepSkyBlue { get { return new XColor(XKnownColor.DeepSkyBlue); } } + + ///Gets a predefined color. + public static XColor DimGray { get { return new XColor(XKnownColor.DimGray); } } + + ///Gets a predefined color. + public static XColor DodgerBlue { get { return new XColor(XKnownColor.DodgerBlue); } } + + ///Gets a predefined color. + public static XColor Firebrick { get { return new XColor(XKnownColor.Firebrick); } } + + ///Gets a predefined color. + public static XColor FloralWhite { get { return new XColor(XKnownColor.FloralWhite); } } + + ///Gets a predefined color. + public static XColor ForestGreen { get { return new XColor(XKnownColor.ForestGreen); } } + + ///Gets a predefined color. + public static XColor Fuchsia { get { return new XColor(XKnownColor.Fuchsia); } } + + ///Gets a predefined color. + public static XColor Gainsboro { get { return new XColor(XKnownColor.Gainsboro); } } + + ///Gets a predefined color. + public static XColor GhostWhite { get { return new XColor(XKnownColor.GhostWhite); } } + + ///Gets a predefined color. + public static XColor Gold { get { return new XColor(XKnownColor.Gold); } } + + ///Gets a predefined color. + public static XColor Goldenrod { get { return new XColor(XKnownColor.Goldenrod); } } + + ///Gets a predefined color. + public static XColor Gray { get { return new XColor(XKnownColor.Gray); } } + + ///Gets a predefined color. + public static XColor Green { get { return new XColor(XKnownColor.Green); } } + + ///Gets a predefined color. + public static XColor GreenYellow { get { return new XColor(XKnownColor.GreenYellow); } } + + ///Gets a predefined color. + public static XColor Honeydew { get { return new XColor(XKnownColor.Honeydew); } } + + ///Gets a predefined color. + public static XColor HotPink { get { return new XColor(XKnownColor.HotPink); } } + + ///Gets a predefined color. + public static XColor IndianRed { get { return new XColor(XKnownColor.IndianRed); } } + + ///Gets a predefined color. + public static XColor Indigo { get { return new XColor(XKnownColor.Indigo); } } + + ///Gets a predefined color. + public static XColor Ivory { get { return new XColor(XKnownColor.Ivory); } } + + ///Gets a predefined color. + public static XColor Khaki { get { return new XColor(XKnownColor.Khaki); } } + + ///Gets a predefined color. + public static XColor Lavender { get { return new XColor(XKnownColor.Lavender); } } + + ///Gets a predefined color. + public static XColor LavenderBlush { get { return new XColor(XKnownColor.LavenderBlush); } } + + ///Gets a predefined color. + public static XColor LawnGreen { get { return new XColor(XKnownColor.LawnGreen); } } + + ///Gets a predefined color. + public static XColor LemonChiffon { get { return new XColor(XKnownColor.LemonChiffon); } } + + ///Gets a predefined color. + public static XColor LightBlue { get { return new XColor(XKnownColor.LightBlue); } } + + ///Gets a predefined color. + public static XColor LightCoral { get { return new XColor(XKnownColor.LightCoral); } } + + ///Gets a predefined color. + public static XColor LightCyan { get { return new XColor(XKnownColor.LightCyan); } } + + ///Gets a predefined color. + public static XColor LightGoldenrodYellow { get { return new XColor(XKnownColor.LightGoldenrodYellow); } } + + ///Gets a predefined color. + public static XColor LightGray { get { return new XColor(XKnownColor.LightGray); } } + + ///Gets a predefined color. + public static XColor LightGreen { get { return new XColor(XKnownColor.LightGreen); } } + + ///Gets a predefined color. + public static XColor LightPink { get { return new XColor(XKnownColor.LightPink); } } + + ///Gets a predefined color. + public static XColor LightSalmon { get { return new XColor(XKnownColor.LightSalmon); } } + + ///Gets a predefined color. + public static XColor LightSeaGreen { get { return new XColor(XKnownColor.LightSeaGreen); } } + + ///Gets a predefined color. + public static XColor LightSkyBlue { get { return new XColor(XKnownColor.LightSkyBlue); } } + + ///Gets a predefined color. + public static XColor LightSlateGray { get { return new XColor(XKnownColor.LightSlateGray); } } + + ///Gets a predefined color. + public static XColor LightSteelBlue { get { return new XColor(XKnownColor.LightSteelBlue); } } + + ///Gets a predefined color. + public static XColor LightYellow { get { return new XColor(XKnownColor.LightYellow); } } + + ///Gets a predefined color. + public static XColor Lime { get { return new XColor(XKnownColor.Lime); } } + + ///Gets a predefined color. + public static XColor LimeGreen { get { return new XColor(XKnownColor.LimeGreen); } } + + ///Gets a predefined color. + public static XColor Linen { get { return new XColor(XKnownColor.Linen); } } + + ///Gets a predefined color. + public static XColor Magenta { get { return new XColor(XKnownColor.Magenta); } } + + ///Gets a predefined color. + public static XColor Maroon { get { return new XColor(XKnownColor.Maroon); } } + + ///Gets a predefined color. + public static XColor MediumAquamarine { get { return new XColor(XKnownColor.MediumAquamarine); } } + + ///Gets a predefined color. + public static XColor MediumBlue { get { return new XColor(XKnownColor.MediumBlue); } } + + ///Gets a predefined color. + public static XColor MediumOrchid { get { return new XColor(XKnownColor.MediumOrchid); } } + + ///Gets a predefined color. + public static XColor MediumPurple { get { return new XColor(XKnownColor.MediumPurple); } } + + ///Gets a predefined color. + public static XColor MediumSeaGreen { get { return new XColor(XKnownColor.MediumSeaGreen); } } + + ///Gets a predefined color. + public static XColor MediumSlateBlue { get { return new XColor(XKnownColor.MediumSlateBlue); } } + + ///Gets a predefined color. + public static XColor MediumSpringGreen { get { return new XColor(XKnownColor.MediumSpringGreen); } } + + ///Gets a predefined color. + public static XColor MediumTurquoise { get { return new XColor(XKnownColor.MediumTurquoise); } } + + ///Gets a predefined color. + public static XColor MediumVioletRed { get { return new XColor(XKnownColor.MediumVioletRed); } } + + ///Gets a predefined color. + public static XColor MidnightBlue { get { return new XColor(XKnownColor.MidnightBlue); } } + + ///Gets a predefined color. + public static XColor MintCream { get { return new XColor(XKnownColor.MintCream); } } + + ///Gets a predefined color. + public static XColor MistyRose { get { return new XColor(XKnownColor.MistyRose); } } + + ///Gets a predefined color. + public static XColor Moccasin { get { return new XColor(XKnownColor.Moccasin); } } + + ///Gets a predefined color. + public static XColor NavajoWhite { get { return new XColor(XKnownColor.NavajoWhite); } } + + ///Gets a predefined color. + public static XColor Navy { get { return new XColor(XKnownColor.Navy); } } + + ///Gets a predefined color. + public static XColor OldLace { get { return new XColor(XKnownColor.OldLace); } } + + ///Gets a predefined color. + public static XColor Olive { get { return new XColor(XKnownColor.Olive); } } + + ///Gets a predefined color. + public static XColor OliveDrab { get { return new XColor(XKnownColor.OliveDrab); } } + + ///Gets a predefined color. + public static XColor Orange { get { return new XColor(XKnownColor.Orange); } } + + ///Gets a predefined color. + public static XColor OrangeRed { get { return new XColor(XKnownColor.OrangeRed); } } + + ///Gets a predefined color. + public static XColor Orchid { get { return new XColor(XKnownColor.Orchid); } } + + ///Gets a predefined color. + public static XColor PaleGoldenrod { get { return new XColor(XKnownColor.PaleGoldenrod); } } + + ///Gets a predefined color. + public static XColor PaleGreen { get { return new XColor(XKnownColor.PaleGreen); } } + + ///Gets a predefined color. + public static XColor PaleTurquoise { get { return new XColor(XKnownColor.PaleTurquoise); } } + + ///Gets a predefined color. + public static XColor PaleVioletRed { get { return new XColor(XKnownColor.PaleVioletRed); } } + + ///Gets a predefined color. + public static XColor PapayaWhip { get { return new XColor(XKnownColor.PapayaWhip); } } + + ///Gets a predefined color. + public static XColor PeachPuff { get { return new XColor(XKnownColor.PeachPuff); } } + + ///Gets a predefined color. + public static XColor Peru { get { return new XColor(XKnownColor.Peru); } } + + ///Gets a predefined color. + public static XColor Pink { get { return new XColor(XKnownColor.Pink); } } + + ///Gets a predefined color. + public static XColor Plum { get { return new XColor(XKnownColor.Plum); } } + + ///Gets a predefined color. + public static XColor PowderBlue { get { return new XColor(XKnownColor.PowderBlue); } } + + ///Gets a predefined color. + public static XColor Purple { get { return new XColor(XKnownColor.Purple); } } + + ///Gets a predefined color. + public static XColor Red { get { return new XColor(XKnownColor.Red); } } + + ///Gets a predefined color. + public static XColor RosyBrown { get { return new XColor(XKnownColor.RosyBrown); } } + + ///Gets a predefined color. + public static XColor RoyalBlue { get { return new XColor(XKnownColor.RoyalBlue); } } + + ///Gets a predefined color. + public static XColor SaddleBrown { get { return new XColor(XKnownColor.SaddleBrown); } } + + ///Gets a predefined color. + public static XColor Salmon { get { return new XColor(XKnownColor.Salmon); } } + + ///Gets a predefined color. + public static XColor SandyBrown { get { return new XColor(XKnownColor.SandyBrown); } } + + ///Gets a predefined color. + public static XColor SeaGreen { get { return new XColor(XKnownColor.SeaGreen); } } + + ///Gets a predefined color. + public static XColor SeaShell { get { return new XColor(XKnownColor.SeaShell); } } + + ///Gets a predefined color. + public static XColor Sienna { get { return new XColor(XKnownColor.Sienna); } } + + ///Gets a predefined color. + public static XColor Silver { get { return new XColor(XKnownColor.Silver); } } + + ///Gets a predefined color. + public static XColor SkyBlue { get { return new XColor(XKnownColor.SkyBlue); } } + + ///Gets a predefined color. + public static XColor SlateBlue { get { return new XColor(XKnownColor.SlateBlue); } } + + ///Gets a predefined color. + public static XColor SlateGray { get { return new XColor(XKnownColor.SlateGray); } } + + ///Gets a predefined color. + public static XColor Snow { get { return new XColor(XKnownColor.Snow); } } + + ///Gets a predefined color. + public static XColor SpringGreen { get { return new XColor(XKnownColor.SpringGreen); } } + + ///Gets a predefined color. + public static XColor SteelBlue { get { return new XColor(XKnownColor.SteelBlue); } } + + ///Gets a predefined color. + public static XColor Tan { get { return new XColor(XKnownColor.Tan); } } + + ///Gets a predefined color. + public static XColor Teal { get { return new XColor(XKnownColor.Teal); } } + + ///Gets a predefined color. + public static XColor Thistle { get { return new XColor(XKnownColor.Thistle); } } + + ///Gets a predefined color. + public static XColor Tomato { get { return new XColor(XKnownColor.Tomato); } } + + ///Gets a predefined color. + public static XColor Transparent { get { return new XColor(XKnownColor.Transparent); } } + + ///Gets a predefined color. + public static XColor Turquoise { get { return new XColor(XKnownColor.Turquoise); } } + + ///Gets a predefined color. + public static XColor Violet { get { return new XColor(XKnownColor.Violet); } } + + ///Gets a predefined color. + public static XColor Wheat { get { return new XColor(XKnownColor.Wheat); } } + + ///Gets a predefined color. + public static XColor White { get { return new XColor(XKnownColor.White); } } + + ///Gets a predefined color. + public static XColor WhiteSmoke { get { return new XColor(XKnownColor.WhiteSmoke); } } + + ///Gets a predefined color. + public static XColor Yellow { get { return new XColor(XKnownColor.Yellow); } } + + ///Gets a predefined color. + public static XColor YellowGreen { get { return new XColor(XKnownColor.YellowGreen); } } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XConvert.cs b/src/PDFsharp/src/PdfSharp/Drawing/XConvert.cs new file mode 100644 index 00000000..3cfc40f8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XConvert.cs @@ -0,0 +1,97 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if CORE +#endif +#if CORE +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Converts XGraphics enums to GDI+ enums. + /// + internal static class XConvert + { +#if GDI +//#if UseGdiObjects + /// + /// Converts XLineJoin to LineJoin. + /// + public static LineJoin ToLineJoin(XLineJoin lineJoin) + { + return GdiLineJoin[(int)lineJoin]; + } + static readonly LineJoin[] GdiLineJoin = new LineJoin[] { LineJoin.Miter, LineJoin.Round, LineJoin.Bevel }; +//#endif +#endif + +#if GDI +//#if UseGdiObjects + /// + /// Converts XLineCap to LineCap. + /// + public static LineCap ToLineCap(XLineCap lineCap) + { + return _gdiLineCap[(int)lineCap]; + } + static readonly LineCap[] _gdiLineCap = new LineCap[] { LineCap.Flat, LineCap.Round, LineCap.Square }; + //#endif +#endif + +#if WPF + /// + /// Converts XLineJoin to PenLineJoin. + /// + public static PenLineJoin ToPenLineJoin(XLineJoin lineJoin) + { + return WpfLineJoin[(int)lineJoin]; + } + static readonly PenLineJoin[] WpfLineJoin = new PenLineJoin[] { PenLineJoin.Miter, PenLineJoin.Round, PenLineJoin.Bevel }; +#endif + +#if WPF + /// + /// Converts XLineCap to PenLineCap. + /// + public static PenLineCap ToPenLineCap(XLineCap lineCap) + { + return WpfLineCap[(int)lineCap]; + } + static readonly PenLineCap[] WpfLineCap = new PenLineCap[] { PenLineCap.Flat, PenLineCap.Round, PenLineCap.Square }; +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs new file mode 100644 index 00000000..1b0a32d6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFont.cs @@ -0,0 +1,862 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// #??? Clean up + +using System; +using System.Diagnostics; +using System.Globalization; +using System.ComponentModel; +#if CORE || GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows.Markup; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif +#if UWP +using UwpFontFamily = Windows.UI.Xaml.Media.FontFamily; +#endif +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; +using PdfSharp.Pdf; + +#if SILVERLIGHT +#pragma warning disable 649 +#endif +// ReSharper disable ConvertToAutoProperty + +namespace PdfSharp.Drawing +{ + /// + /// Defines an object used to draw text. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public sealed class XFont + { + /// + /// Initializes a new instance of the class. + /// + /// Name of the font family. + /// The em size. + public XFont(string familyName, double emSize) + : this(familyName, emSize, XFontStyle.Regular, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the font family. + /// The em size. + /// The font style. + public XFont(string familyName, double emSize, XFontStyle style) + : this(familyName, emSize, style, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the font family. + /// The em size. + /// The font style. + /// Additional PDF options. + public XFont(string familyName, double emSize, XFontStyle style, XPdfFontOptions pdfOptions) + { + _familyName = familyName; + _emSize = emSize; + _style = style; + _pdfOptions = pdfOptions; + Initialize(); + } + + /// + /// Initializes a new instance of the class with enforced style simulation. + /// Only for testing PDFsharp. + /// + internal XFont(string familyName, double emSize, XFontStyle style, XPdfFontOptions pdfOptions, XStyleSimulations styleSimulations) + { + _familyName = familyName; + _emSize = emSize; + _style = style; + _pdfOptions = pdfOptions; + OverrideStyleSimulations = true; + StyleSimulations = styleSimulations; + Initialize(); + } + +#if CORE || GDI + /// + /// Initializes a new instance of the class from a System.Drawing.FontFamily. + /// + /// The System.Drawing.FontFamily. + /// The em size. + /// The font style. + public XFont(GdiFontFamily fontFamily, double emSize, XFontStyle style) + : this(fontFamily, emSize, style, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Drawing.FontFamily. + /// + /// The System.Drawing.FontFamily. + /// The em size. + /// The font style. + /// Additional PDF options. + public XFont(GdiFontFamily fontFamily, double emSize, XFontStyle style, XPdfFontOptions pdfOptions) + { + _familyName = fontFamily.Name; + _gdiFontFamily = fontFamily; + _emSize = emSize; + _style = style; + _pdfOptions = pdfOptions; + InitializeFromGdi(); + } + + /// + /// Initializes a new instance of the class from a System.Drawing.Font. + /// + /// The System.Drawing.Font. + public XFont(GdiFont font) + : this(font, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Drawing.Font. + /// + /// The System.Drawing.Font. + /// Additional PDF options. + public XFont(GdiFont font, XPdfFontOptions pdfOptions) + { + if (font.Unit != GraphicsUnit.World) + throw new ArgumentException("Font must use GraphicsUnit.World."); + _gdiFont = font; + Debug.Assert(font.Name == font.FontFamily.Name); + _familyName = font.Name; + _emSize = font.Size; + _style = FontStyleFrom(font); + _pdfOptions = pdfOptions; + InitializeFromGdi(); + } +#endif + +#if WPF && !SILVERLIGHT + /// + /// Initializes a new instance of the class from a System.Windows.Media.FontFamily. + /// + /// The System.Windows.Media.FontFamily. + /// The em size. + /// The font style. + public XFont(WpfFontFamily fontFamily, double emSize, XFontStyle style) + : this(fontFamily, emSize, style, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Drawing.FontFamily. + /// + /// The System.Windows.Media.FontFamily. + /// The em size. + /// The font style. + /// Additional PDF options. + public XFont(WpfFontFamily fontFamily, double emSize, XFontStyle style, XPdfFontOptions pdfOptions) + { +#if !SILVERLIGHT + _familyName = fontFamily.FamilyNames[XmlLanguage.GetLanguage("en-US")]; +#else + // Best we can do in Silverlight. + _familyName = fontFamily.Source; +#endif + _wpfFontFamily = fontFamily; + _emSize = emSize; + _style = style; + _pdfOptions = pdfOptions; + InitializeFromWpf(); + } + + /// + /// Initializes a new instance of the class from a System.Windows.Media.Typeface. + /// + /// The System.Windows.Media.Typeface. + /// The em size. + public XFont(WpfTypeface typeface, double emSize) + : this(typeface, emSize, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Windows.Media.Typeface. + /// + /// The System.Windows.Media.Typeface. + /// The em size. + /// Additional PDF options. + public XFont(WpfTypeface typeface, double emSize, XPdfFontOptions pdfOptions) + { + _wpfTypeface = typeface; + //Debug.Assert(font.Name == font.FontFamily.Name); + //_familyName = font.Name; + _emSize = emSize; + _pdfOptions = pdfOptions; + InitializeFromWpf(); + } +#endif + +#if UWP_ + /// + /// Initializes a new instance of the class from a System.Drawing.FontFamily. + /// + /// The System.Drawing.FontFamily. + /// The em size. + /// The font style. + public XFont(UwpFontFamily fontFamily, double emSize, XFontStyle style) + : this(fontFamily, emSize, style, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Drawing.FontFamily. + /// + /// The System.Drawing.FontFamily. + /// The em size. + /// The font style. + /// Additional PDF options. + public XFont(UwpFontFamily fontFamily, double emSize, XFontStyle style, XPdfFontOptions pdfOptions) + { + _familyName = fontFamily.Source; + _gdiFontFamily = fontFamily; + _emSize = emSize; + _style = style; + _pdfOptions = pdfOptions; + InitializeFromGdi(); + } + + /// + /// Initializes a new instance of the class from a System.Drawing.Font. + /// + /// The System.Drawing.Font. + public XFont(GdiFont font) + : this(font, new XPdfFontOptions(GlobalFontSettings.DefaultFontEncoding)) + { } + + /// + /// Initializes a new instance of the class from a System.Drawing.Font. + /// + /// The System.Drawing.Font. + /// Additional PDF options. + public XFont(GdiFont font, XPdfFontOptions pdfOptions) + { + if (font.Unit != GraphicsUnit.World) + throw new ArgumentException("Font must use GraphicsUnit.World."); + _gdiFont = font; + Debug.Assert(font.Name == font.FontFamily.Name); + _familyName = font.Name; + _emSize = font.Size; + _style = FontStyleFrom(font); + _pdfOptions = pdfOptions; + InitializeFromGdi(); + } +#endif + + //// Methods + //public Font(Font prototype, FontStyle newStyle); + //public Font(FontFamily family, float emSize); + //public Font(string familyName, float emSize); + //public Font(FontFamily family, float emSize, FontStyle style); + //public Font(FontFamily family, float emSize, GraphicsUnit unit); + //public Font(string familyName, float emSize, FontStyle style); + //public Font(string familyName, float emSize, GraphicsUnit unit); + //public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit); + //public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit); + ////public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet); + ////public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet); + ////public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont); + ////public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont); + //public object Clone(); + //private static FontFamily CreateFontFamilyWithFallback(string familyName); + //private void Dispose(bool disposing); + //public override bool Equals(object obj); + //protected override void Finalize(); + //public static Font FromHdc(IntPtr hdc); + //public static Font FromHfont(IntPtr hfont); + //public static Font FromLogFont(object lf); + //public static Font FromLogFont(object lf, IntPtr hdc); + //public override int GetHashCode(); + + /// + /// Initializes this instance by computing the glyph typeface, font family, font source and TrueType fontface. + /// (PDFsharp currently only deals with TrueType fonts.) + /// + void Initialize() + { +//#if DEBUG +// if (_familyName == "Segoe UI Semilight" && (_style & XFontStyle.BoldItalic) == XFontStyle.Italic) +// GetType(); +//#endif + + FontResolvingOptions fontResolvingOptions = OverrideStyleSimulations + ? new FontResolvingOptions(_style, StyleSimulations) + : new FontResolvingOptions(_style); + + // HACK: 'PlatformDefault' is used in unit test code. + if (StringComparer.OrdinalIgnoreCase.Compare(_familyName, GlobalFontSettings.DefaultFontName) == 0) + { +#if CORE || GDI || WPF + _familyName = "Calibri"; +#endif + } + + // In principle an XFont is an XGlyphTypeface plus an em-size. + _glyphTypeface = XGlyphTypeface.GetOrCreateFrom(_familyName, fontResolvingOptions); +#if GDI // TODO: In CORE build it is not necessary to create a GDI font at all + // Create font by using font family. + XFontSource fontSource; // Not needed here. + _gdiFont = FontHelper.CreateFont(_familyName, (float)_emSize, (GdiFontStyle)(_style & XFontStyle.BoldItalic), out fontSource); +#endif +#if WPF && !SILVERLIGHT // Pure WPF + _wpfFontFamily = _glyphTypeface.FontFamily.WpfFamily; + _wpfTypeface = _glyphTypeface.WpfTypeface; + + if (_wpfFontFamily == null) + _wpfFontFamily = new WpfFontFamily(Name); + + if (_wpfTypeface == null) + _wpfTypeface = FontHelper.CreateTypeface(WpfFontFamily, _style); +#endif +#if WPF && SILVERLIGHT_ // Pure Silverlight 5 + if (GlyphTypeface == null) + { + //Debug.Assert(Typeface == null); + // #P F C + //GlyphTypeface = XPrivateFontCollection.TryGetXGlyphTypeface(Name, _style); + //if (GlyphTypeface == null) + //{ + // // HACK: Just make it work... + // GlyphTypeface = GlobalFontSettings.TryGetXGlyphTypeface(Name, _style, out Data); + //} +#if DEBUG + if (GlyphTypeface == null) + throw new Exception("No font: " + Name); +#endif + _wpfFamily = GlyphTypeface.FontFamily; + } + + //if (Family == null) + // Family = new System.Windows.Media.FontFamily(Name); + + //if (Typeface == null) + // Typeface = FontHelper.CreateTypeface(Family, _style); +#endif + CreateDescriptorAndInitializeFontMetrics(); + } + +#if CORE || GDI + /// + /// A GDI+ font object is used to setup the internal font objects. + /// + void InitializeFromGdi() + { + try + { + Lock.EnterFontFactory(); + if (_gdiFontFamily != null) + { + // Create font based on its family. + _gdiFont = new Font(_gdiFontFamily, (float)_emSize, (GdiFontStyle)_style, GraphicsUnit.World); + } + + if (_gdiFont != null) + { +#if DEBUG_ + string name1 = _gdiFont.Name; + string name2 = _gdiFont.OriginalFontName; + string name3 = _gdiFont.SystemFontName; +#endif + _familyName = _gdiFont.FontFamily.Name; + // TODO: _glyphTypeface = XGlyphTypeface.GetOrCreateFrom(_gdiFont); + } + else + { + Debug.Assert(false); + } + + if (_glyphTypeface == null) + _glyphTypeface = XGlyphTypeface.GetOrCreateFromGdi(_gdiFont); + + CreateDescriptorAndInitializeFontMetrics(); + } + finally { Lock.ExitFontFactory(); } + } +#endif + +#if WPF && !SILVERLIGHT + void InitializeFromWpf() + { + if (_wpfFontFamily != null) + { + _wpfTypeface = FontHelper.CreateTypeface(_wpfFontFamily, _style); + } + + if (_wpfTypeface != null) + { + _familyName = _wpfTypeface.FontFamily.FamilyNames[XmlLanguage.GetLanguage("en-US")]; + _glyphTypeface = XGlyphTypeface.GetOrCreateFromWpf(_wpfTypeface); + } + else + { + Debug.Assert(false); + } + + if (_glyphTypeface == null) + _glyphTypeface = XGlyphTypeface.GetOrCreateFrom(_familyName, new FontResolvingOptions(_style)); + + CreateDescriptorAndInitializeFontMetrics(); + } +#endif + + /// + /// Code separated from Metric getter to make code easier to debug. + /// (Setup properties in their getters caused side effects during debugging because Visual Studio calls a getter + /// to early to show its value in a debugger window.) + /// + void CreateDescriptorAndInitializeFontMetrics() // TODO: refactor + { + Debug.Assert(_fontMetrics == null, "InitializeFontMetrics() was already called."); + _descriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptorFor(this); //_familyName, _style, _glyphTypeface.Fontface); + _fontMetrics = new XFontMetrics(_descriptor.FontName, _descriptor.UnitsPerEm, _descriptor.Ascender, _descriptor.Descender, + _descriptor.Leading, _descriptor.LineSpacing, _descriptor.CapHeight, _descriptor.XHeight, _descriptor.StemV, 0, 0, 0, + _descriptor.UnderlinePosition, _descriptor.UnderlineThickness, _descriptor.StrikeoutPosition, _descriptor.StrikeoutSize); + + XFontMetrics fm = Metrics; + + // Already done in CreateDescriptorAndInitializeFontMetrics. + //if (_descriptor == null) + // _descriptor = (OpenTypeDescriptor)FontDescriptorStock.Global.CreateDescriptor(this); //(Name, (XGdiFontStyle)Font.Style); + + UnitsPerEm = _descriptor.UnitsPerEm; + CellAscent = _descriptor.Ascender; + CellDescent = _descriptor.Descender; + CellSpace = _descriptor.LineSpacing; + +#if DEBUG_ && GDI + int gdiValueUnitsPerEm = Font.FontFamily.GetEmHeight(Font.Style); + Debug.Assert(gdiValueUnitsPerEm == UnitsPerEm); + int gdiValueAscent = Font.FontFamily.GetCellAscent(Font.Style); + Debug.Assert(gdiValueAscent == CellAscent); + int gdiValueDescent = Font.FontFamily.GetCellDescent(Font.Style); + Debug.Assert(gdiValueDescent == CellDescent); + int gdiValueLineSpacing = Font.FontFamily.GetLineSpacing(Font.Style); + Debug.Assert(gdiValueLineSpacing == CellSpace); +#endif +#if DEBUG_ && WPF && !SILVERLIGHT + int wpfValueLineSpacing = (int)Math.Round(Family.LineSpacing * _descriptor.UnitsPerEm); + Debug.Assert(wpfValueLineSpacing == CellSpace); +#endif + Debug.Assert(fm.UnitsPerEm == _descriptor.UnitsPerEm); + } + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// + /// Gets the XFontFamily object associated with this XFont object. + /// + [Browsable(false)] + public XFontFamily FontFamily + { + get { return _glyphTypeface.FontFamily; } + } + + /// + /// WRONG: Gets the face name of this Font object. + /// Indeed it returns the font family name. + /// + // [Obsolete("This function returns the font family name, not the face name. Use xxx.FontFamily.Name or xxx.FaceName")] + public string Name + { + get { return _glyphTypeface.FontFamily.Name; } + } + + internal string FaceName + { + get { return _glyphTypeface.FaceName; } + } + + /// + /// Gets the em-size of this font measured in the unit of this font object. + /// + public double Size + { + get { return _emSize; } + } + readonly double _emSize; + + /// + /// Gets style information for this Font object. + /// + [Browsable(false)] + public XFontStyle Style + { + get { return _style; } + } + readonly XFontStyle _style; + + /// + /// Indicates whether this XFont object is bold. + /// + public bool Bold + { + get { return (_style & XFontStyle.Bold) == XFontStyle.Bold; } + } + + /// + /// Indicates whether this XFont object is italic. + /// + public bool Italic + { + get { return (_style & XFontStyle.Italic) == XFontStyle.Italic; } + } + + /// + /// Indicates whether this XFont object is stroke out. + /// + public bool Strikeout + { + get { return (_style & XFontStyle.Strikeout) == XFontStyle.Strikeout; } + } + + /// + /// Indicates whether this XFont object is underlined. + /// + public bool Underline + { + get { return (_style & XFontStyle.Underline) == XFontStyle.Underline; } + } + + /// + /// Temporary HACK for XPS to PDF converter. + /// + internal bool IsVertical + { + get { return _isVertical; } + set { _isVertical = value; } + } + bool _isVertical; + + + /// + /// Gets the PDF options of the font. + /// + public XPdfFontOptions PdfOptions + { + get { return _pdfOptions ?? (_pdfOptions = new XPdfFontOptions()); } + } + XPdfFontOptions _pdfOptions; + + /// + /// Indicates whether this XFont is encoded as Unicode. + /// + internal bool Unicode + { + get { return _pdfOptions != null && _pdfOptions.FontEncoding == PdfFontEncoding.Unicode; } + } + + /// + /// Gets the cell space for the font. The CellSpace is the line spacing, the sum of CellAscent and CellDescent and optionally some extra space. + /// + public int CellSpace + { + get { return _cellSpace; } + internal set { _cellSpace = value; } + } + int _cellSpace; + + /// + /// Gets the cell ascent, the area above the base line that is used by the font. + /// + public int CellAscent + { + get { return _cellAscent; } + internal set { _cellAscent = value; } + } + int _cellAscent; + + /// + /// Gets the cell descent, the area below the base line that is used by the font. + /// + public int CellDescent + { + get { return _cellDescent; } + internal set { _cellDescent = value; } + } + int _cellDescent; + + /// + /// Gets the font metrics. + /// + /// The metrics. + public XFontMetrics Metrics + { + get + { + // Code moved to InitializeFontMetrics(). + //if (_fontMetrics == null) + //{ + // FontDescriptor descriptor = FontDescriptorStock.Global.CreateDescriptor(this); + // _fontMetrics = new XFontMetrics(descriptor.FontName, descriptor.UnitsPerEm, descriptor.Ascender, descriptor.Descender, + // descriptor.Leading, descriptor.LineSpacing, descriptor.CapHeight, descriptor.XHeight, descriptor.StemV, 0, 0, 0); + //} + Debug.Assert(_fontMetrics != null, "InitializeFontMetrics() not yet called."); + return _fontMetrics; + } + } + XFontMetrics _fontMetrics; + + /// + /// Returns the line spacing, in pixels, of this font. The line spacing is the vertical distance + /// between the base lines of two consecutive lines of text. Thus, the line spacing includes the + /// blank space between lines along with the height of the character itself. + /// + public double GetHeight() + { + double value = CellSpace * _emSize / UnitsPerEm; +#if CORE || NETFX_CORE || UWP || DNC10 + return value; +#endif +#if GDI && !WPF +#if DEBUG_ + double gdiValue = Font.GetHeight(); + Debug.Assert(DoubleUtil.AreRoughlyEqual(gdiValue, value, 5)); +#endif + return value; +#endif +#if WPF && !GDI + return value; +#endif +#if WPF && GDI // Testing only + return value; +#endif + } + + /// + /// Returns the line spacing, in the current unit of a specified Graphics object, of this font. + /// The line spacing is the vertical distance between the base lines of two consecutive lines of + /// text. Thus, the line spacing includes the blank space between lines along with the height of + /// + [Obsolete("Use GetHeight() without parameter.")] + public double GetHeight(XGraphics graphics) + { +#if true + throw new InvalidOperationException("Honestly: Use GetHeight() without parameter!"); +#else +#if CORE || NETFX_CORE + double value = CellSpace * _emSize / UnitsPerEm; + return value; +#endif +#if GDI && !WPF + if (graphics._gfx != null) // #MediumTrust + { + double value = Font.GetHeight(graphics._gfx); + Debug.Assert(value == Font.GetHeight(graphics._gfx.DpiY)); + double value2 = CellSpace * _emSize / UnitsPerEm; + Debug.Assert(value - value2 < 1e-3, "??"); + return Font.GetHeight(graphics._gfx); + } + return CellSpace * _emSize / UnitsPerEm; +#endif +#if WPF && !GDI + double value = CellSpace * _emSize / UnitsPerEm; + return value; +#endif +#if GDI && WPF // Testing only + if (graphics.TargetContext == XGraphicTargetContext.GDI) + { +#if DEBUG + double value = Font.GetHeight(graphics._gfx); + + // 2355*(0.3/2048)*96 = 33.11719 + double myValue = CellSpace * (_emSize / (96 * UnitsPerEm)) * 96; + myValue = CellSpace * _emSize / UnitsPerEm; + //Debug.Assert(value == myValue, "??"); + //Debug.Assert(value - myValue < 1e-3, "??"); +#endif + return Font.GetHeight(graphics._gfx); + } + + if (graphics.TargetContext == XGraphicTargetContext.WPF) + { + double value = CellSpace * _emSize / UnitsPerEm; + return value; + } + // ReSharper disable HeuristicUnreachableCode + Debug.Fail("Either GDI or WPF."); + return 0; + // ReSharper restore HeuristicUnreachableCode +#endif +#endif + } + + /// + /// Gets the line spacing of this font. + /// + [Browsable(false)] + public int Height + { + // Implementation from System.Drawing.Font.cs + get { return (int)Math.Ceiling(GetHeight()); } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + internal XGlyphTypeface GlyphTypeface + { + get { return _glyphTypeface; } + } + XGlyphTypeface _glyphTypeface; + + + internal OpenTypeDescriptor Descriptor + { + get { return _descriptor; } + private set { _descriptor = value; } + } + OpenTypeDescriptor _descriptor; + + + internal string FamilyName + { + get { return _familyName; } + } + string _familyName; + + + internal int UnitsPerEm + { + get { return _unitsPerEm; } + private set { _unitsPerEm = value; } + } + internal int _unitsPerEm; + + /// + /// Override style simulations by using the value of StyleSimulations. + /// + internal bool OverrideStyleSimulations; + + /// + /// Used to enforce style simulations by renderer. For development purposes only. + /// + internal XStyleSimulations StyleSimulations; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +#if CORE || GDI + /// + /// Gets the GDI family. + /// + /// The GDI family. + public GdiFontFamily GdiFontFamily + { + get { return _gdiFontFamily; } + } + readonly GdiFontFamily _gdiFontFamily; + + internal GdiFont GdiFont + { + get { return _gdiFont; } + } + Font _gdiFont; + + internal static XFontStyle FontStyleFrom(GdiFont font) + { + return + (font.Bold ? XFontStyle.Bold : 0) | + (font.Italic ? XFontStyle.Italic : 0) | + (font.Strikeout ? XFontStyle.Strikeout : 0) | + (font.Underline ? XFontStyle.Underline : 0); + } + +#if true || UseGdiObjects + /// + /// Implicit conversion form Font to XFont + /// + public static implicit operator XFont(GdiFont font) + { + return new XFont(font); + } +#endif +#endif + +#if WPF + /// + /// Gets the WPF font family. + /// Can be null. + /// + internal WpfFontFamily WpfFontFamily + { + get { return _wpfFontFamily; } + } + WpfFontFamily _wpfFontFamily; + + internal WpfTypeface WpfTypeface + { + get { return _wpfTypeface; } + } + WpfTypeface _wpfTypeface; +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + /// + /// Cache PdfFontTable.FontSelector to speed up finding the right PdfFont + /// if this font is used more than once. + /// + internal string Selector + { + get { return _selector; } + set { _selector = value; } + } + string _selector; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get { return String.Format(CultureInfo.InvariantCulture, "font=('{0}' {1:0.##})", Name, Size); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontFamily.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontFamily.cs new file mode 100644 index 00000000..e2ccbfea --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontFamily.cs @@ -0,0 +1,319 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if CORE || GDI +using System.Drawing; +using GdiFont = System.Drawing.Font; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows.Media; +using System.Windows.Markup; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfFontStyle = System.Windows.FontStyle; +#endif +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; + +namespace PdfSharp.Drawing +{ + /// + /// Defines a group of typefaces having a similar basic design and certain variations in styles. + /// + public sealed class XFontFamily + { + /// + /// Initializes a new instance of the class. + /// + /// The family name of a font. + public XFontFamily(string familyName) + { + FamilyInternal = FontFamilyInternal.GetOrCreateFromName(familyName, true); + } + + internal XFontFamily(string familyName, bool createPlatformObjects) + { + FamilyInternal = FontFamilyInternal.GetOrCreateFromName(familyName, createPlatformObjects); + } + + /// + /// Initializes a new instance of the class from FontFamilyInternal. + /// + XFontFamily(FontFamilyInternal fontFamilyInternal) + { + FamilyInternal = fontFamilyInternal; + } + +#if CORE || GDI + //public XFontFamily(GdiFontFamily gdiFontFamily) + //{ + // FamilyInternal = FontFamilyInternal.GetOrCreateFromGdi(gdiFontFamily); + //} +#endif + +#if WPF + //public XFontFamily(WpfFontFamily wpfFontFamily) + //{ + // FamilyInternal = FontFamilyInternal.GetOrCreateFromWpf(wpfFontFamily); + // //// HACK + // //int idxHash = _name.LastIndexOf('#'); + // //if (idxHash > 0) + // // _name = _name.Substring(idxHash + 1); + // //_wpfFamily = family; + //} +#endif + + internal static XFontFamily CreateFromName_not_used(string name, bool createPlatformFamily) + { + XFontFamily fontFamily = new XFontFamily(name); + if (createPlatformFamily) + { +#if GDI + //fontFamily._gdiFamily = new System.Drawing.FontFamily(name); +#endif +#if WPF + //fontFamily._wpfFamily = new System.Windows.Media.FontFamily(name); +#endif + } + return fontFamily; + } + + /// + /// An XGlyphTypeface for a font source that comes from a custom font resolver + /// creates a solitary font family exclusively for it. + /// + internal static XFontFamily GetOrCreateFontFamily(string name) + { + // Custom font resolver face names must not clash with platform family names. + FontFamilyInternal fontFamilyInternal = FontFamilyCache.GetFamilyByName(name); + if (fontFamilyInternal == null) + { + fontFamilyInternal = FontFamilyInternal.GetOrCreateFromName(name, false); + fontFamilyInternal = FontFamilyCache.CacheOrGetFontFamily(fontFamilyInternal); + } + + // Create font family and save it in cache. Do not try to create platform objects. + return new XFontFamily(fontFamilyInternal); + } + +#if CORE || GDI + internal static XFontFamily GetOrCreateFromGdi(GdiFont font) + { + FontFamilyInternal fontFamilyInternal = FontFamilyInternal.GetOrCreateFromGdi(font.FontFamily); + return new XFontFamily(fontFamilyInternal); + } +#endif + +#if WPF + internal static XFontFamily GetOrCreateFromWpf(WpfFontFamily wpfFontFamily) + { + FontFamilyInternal fontFamilyInternal = FontFamilyInternal.GetOrCreateFromWpf(wpfFontFamily); + return new XFontFamily(fontFamilyInternal); + } +#endif +#if SILVERLIGHT + //internal static XFontFamily CreateFromWpf(System.Windows.Media.FontFamily wpfFontFamily) + //{ + // XFontFamily fontFamily = new XFontFamily(wpfFontFamily.FamilyNames[XmlLanguage.GetLanguage("en")]); + // fontFamily._wpfFamily = wpfFontFamily; + // return fontFamily; + //} +#endif + + /// + /// Gets the name of the font family. + /// + public string Name + { + get { return FamilyInternal.Name; } + } + +#if true__ + public double LineSpacing + { + get + { + WpfFamily.FamilyTypefaces[0].UnderlineThickness + } + } + +#endif + + /// + /// Returns the cell ascent, in design units, of the XFontFamily object of the specified style. + /// + public int GetCellAscent(XFontStyle style) + { + OpenTypeDescriptor descriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptor(Name, style); + int result = descriptor.Ascender; +#if DEBUG_ && GDI + int gdiValue = _gdiFamily.GetCellAscent((FontStyle)style); + Debug.Assert(gdiValue == result); +#endif + return result; + } + + /// + /// Returns the cell descent, in design units, of the XFontFamily object of the specified style. + /// + public int GetCellDescent(XFontStyle style) + { + OpenTypeDescriptor descriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptor(Name, style); + int result = descriptor.Descender; +#if DEBUG_ && GDI + int gdiValue = _gdiFamily.GetCellDescent((FontStyle)style); + Debug.Assert(gdiValue == result); +#endif + return result; + } + + /// + /// Gets the height, in font design units, of the em square for the specified style. + /// + public int GetEmHeight(XFontStyle style) + { + OpenTypeDescriptor descriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptor(Name, style); + int result = descriptor.UnitsPerEm; +#if DEBUG_ && GDI + int gdiValue = _gdiFamily.GetEmHeight((FontStyle)style); + Debug.Assert(gdiValue == result); +#endif +#if DEBUG_ + int headValue = descriptor.FontFace.head.unitsPerEm; + Debug.Assert(headValue == result); +#endif + return result; + } + + /// + /// Returns the line spacing, in design units, of the FontFamily object of the specified style. + /// The line spacing is the vertical distance between the base lines of two consecutive lines of text. + /// + public int GetLineSpacing(XFontStyle style) + { + OpenTypeDescriptor descriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptor(Name, style); + int result = descriptor.LineSpacing; +#if DEBUG_ && GDI + int gdiValue = _gdiFamily.GetLineSpacing((FontStyle)style); + Debug.Assert(gdiValue == result); +#endif +#if DEBUG_ && WPF && !SILVERLIGHT + int wpfValue = (int)Math.Round(_wpfFamily.LineSpacing * GetEmHeight(style)); + Debug.Assert(wpfValue == result); +#endif + return result; + } + + //public string GetName(int language); + + /// + /// Indicates whether the specified FontStyle enumeration is available. + /// + public bool IsStyleAvailable(XFontStyle style) + { + XGdiFontStyle xStyle = ((XGdiFontStyle)style) & XGdiFontStyle.BoldItalic; +#if CORE + throw new InvalidOperationException("In CORE build it is the responsibility of the developer to provide all required font faces."); +#endif +#if GDI && !WPF + if (GdiFamily != null) + return GdiFamily.IsStyleAvailable((GdiFontStyle)xStyle); + return false; +#endif +#if WPF && !GDI + if (WpfFamily != null) + return FontHelper.IsStyleAvailable(this, xStyle); + return false; +#endif +#if WPF && GDI +#if DEBUG + //bool gdiResult = _gdiFamily.IsStyle Available((FontStyle)style); + //bool wpfResult = FontHelper.IsStyle Available(this, style); + //// TODOWPF: check when fails + //Debug.Assert(gdiResult == wpfResult, "GDI+ and WPF provide different values."); +#endif + return FontHelper.IsStyleAvailable(this, xStyle); +#endif +#if NETFX_CORE || UWP || DNC10 + throw new InvalidOperationException("In NETFX_CORE build it is the responsibility of the developer to provide all required font faces."); +#endif + } + + /// + /// Returns an array that contains all the FontFamily objects associated with the current graphics context. + /// + [Obsolete("Use platform API directly.")] + public static XFontFamily[] Families + { + get + { + throw new InvalidOperationException("Obsolete and not implemted any more."); + } + } + + /// + /// Returns an array that contains all the FontFamily objects available for the specified + /// graphics context. + /// + [Obsolete("Use platform API directly.")] + public static XFontFamily[] GetFamilies(XGraphics graphics) + { + throw new InvalidOperationException("Obsolete and not implemted any more."); + } + +#if GDI + /// + /// Gets the underlying GDI+ font family object. + /// Is null if the font was created by a font resolver. + /// + internal GdiFontFamily GdiFamily + { + get { return FamilyInternal.GdiFamily; } + } +#endif + +#if WPF + /// + /// Gets the underlying WPF font family object. + /// Is null if the font was created by a font resolver. + /// + internal WpfFontFamily WpfFamily + { + get { return FamilyInternal.WpfFamily; } + } +#endif + + /// + /// The implementation sigleton of font family; + /// + internal FontFamilyInternal FamilyInternal; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontMetrics.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontMetrics.cs new file mode 100644 index 00000000..e19ff5fb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontMetrics.cs @@ -0,0 +1,203 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Collects information of a font. + /// + public sealed class XFontMetrics + { + internal XFontMetrics(string name, int unitsPerEm, int ascent, int descent, int leading, int lineSpacing, + int capHeight, int xHeight, int stemV, int stemH, int averageWidth, int maxWidth , + int underlinePosition, int underlineThickness, int strikethroughPosition, int strikethroughThickness) + { + _name = name; + _unitsPerEm = unitsPerEm; + _ascent = ascent; + _descent = descent; + _leading = leading; + _lineSpacing = lineSpacing; + _capHeight = capHeight; + _xHeight = xHeight; + _stemV = stemV; + _stemH = stemH; + _averageWidth = averageWidth; + _maxWidth = maxWidth; + _underlinePosition = underlinePosition; + _underlineThickness = underlineThickness; + _strikethroughPosition = strikethroughPosition; + _strikethroughThickness = strikethroughThickness; + } + + /// + /// Gets the font name. + /// + public string Name + { + get { return _name; } + } + readonly string _name; + + /// + /// Gets the ascent value. + /// + public int UnitsPerEm + { + get { return _unitsPerEm; } + } + readonly int _unitsPerEm; + + /// + /// Gets the ascent value. + /// + public int Ascent + { + get { return _ascent; } + } + readonly int _ascent; + + /// + /// Gets the descent value. + /// + public int Descent + { + get { return _descent; } + } + readonly int _descent; + + /// + /// Gets the average width. + /// + public int AverageWidth + { + get { return _averageWidth; } + } + readonly int _averageWidth; + + /// + /// Gets the height of capital letters. + /// + public int CapHeight + { + get { return _capHeight; } + } + readonly int _capHeight; + + /// + /// Gets the leading value. + /// + public int Leading + { + get { return _leading; } + } + readonly int _leading; + + /// + /// Gets the line spacing value. + /// + public int LineSpacing + { + get { return _lineSpacing; } + } + readonly int _lineSpacing; + + /// + /// Gets the maximum width of a character. + /// + public int MaxWidth + { + get { return _maxWidth; } + } + readonly int _maxWidth; + + /// + /// Gets an internal value. + /// + public int StemH + { + get { return _stemH; } + } + readonly int _stemH; + + /// + /// Gets an internal value. + /// + public int StemV + { + get { return _stemV; } + } + readonly int _stemV; + + /// + /// Gets the height of a lower-case character. + /// + public int XHeight + { + get { return _xHeight; } + } + readonly int _xHeight; + + /// + /// Gets the underline position. + /// + public int UnderlinePosition + { + get { return _underlinePosition; } + } + readonly int _underlinePosition; + + /// + /// Gets the underline thicksness. + /// + public int UnderlineThickness + { + get { return _underlineThickness; } + } + readonly int _underlineThickness; + + /// + /// Gets the strikethrough position. + /// + public int StrikethroughPosition + { + get { return _strikethroughPosition; } + } + readonly int _strikethroughPosition; + + /// + /// Gets the strikethrough thicksness. + /// + public int StrikethroughThickness + { + get { return _strikethroughThickness; } + } + readonly int _strikethroughThickness; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontSource.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontSource.cs new file mode 100644 index 00000000..6b3a5781 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontSource.cs @@ -0,0 +1,299 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using PdfSharp.Fonts; +#if CORE || GDI +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif +using PdfSharp.Internal; +using PdfSharp.Fonts.OpenType; + +namespace PdfSharp.Drawing +{ + /// + /// The bytes of a font file. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal class XFontSource + { + // Implementation Notes + // + // * XFontSource represents a single font (file) in memory. + // * An XFontSource hold a reference to it OpenTypeFontface. + // * To prevent large heap fragmentation this class must exists only once. + // * TODO: ttcf + + // Signature of a true type collection font. + const uint ttcf = 0x66637474; + + XFontSource(byte[] bytes, ulong key) + { + _fontName = null; + _bytes = bytes; + _key = key; + } + + /// + /// Gets an existing font source or creates a new one. + /// A new font source is cached in font factory. + /// + public static XFontSource GetOrCreateFrom(byte[] bytes) + { + ulong key = FontHelper.CalcChecksum(bytes); + XFontSource fontSource; + if (!FontFactory.TryGetFontSourceByKey(key, out fontSource)) + { + fontSource = new XFontSource(bytes, key); + // Theoretically the font source could be created by a differend thread in the meantime. + fontSource = FontFactory.CacheFontSource(fontSource); + } + return fontSource; + } + +#if CORE || GDI + internal static XFontSource GetOrCreateFromGdi(string typefaceKey, GdiFont gdiFont) + { + byte[] bytes = ReadFontBytesFromGdi(gdiFont); + XFontSource fontSource = GetOrCreateFrom(typefaceKey, bytes); + return fontSource; + } + + static byte[] ReadFontBytesFromGdi(GdiFont gdiFont) + { + // Weird: LastError is always 123 or 127. Comment out Debug.Assert. + int error = Marshal.GetLastWin32Error(); + //Debug.Assert(error == 0); + error = Marshal.GetLastWin32Error(); + //Debug.Assert(error == 0); + + IntPtr hfont = gdiFont.ToHfont(); +#if true + IntPtr hdc = NativeMethods.GetDC(IntPtr.Zero); +#else + NativeMethods.LOGFONT logFont = new NativeMethods.LOGFONT(); + logFont.lfHeight = 30; + logFont.lfWidth = 0; + logFont.lfEscapement = 0; + logFont.lfOrientation = 0; + logFont.lfWeight = 400; + logFont.lfItalic = 0; + logFont.lfUnderline = 0; + logFont.lfStrikeOut = 0; + logFont.lfCharSet = 0; + logFont.lfOutPrecision = 0; + logFont.lfClipPrecision = 0; + logFont.lfQuality = 0; + logFont.lfPitchAndFamily = 0; + logFont.lfFaceName = "Arial"; + + gdiFont.ToLogFont(logFont); + + hfont = NativeMethods.CreateFontIndirect(logFont); + + + // IntPtr hdc = NativeMethods.CreateDC("DISPLAY", null, null, IntPtr.Zero); + IntPtr hdc = NativeMethods.CreateCompatibleDC(IntPtr.Zero); +#endif + error = Marshal.GetLastWin32Error(); + //Debug.Assert(error == 0); + + IntPtr oldFont = NativeMethods.SelectObject(hdc, hfont); + error = Marshal.GetLastWin32Error(); + //Debug.Assert(error == 0); + + // Get size of the font file. + bool isTtcf = false; + // In Azure I get 0xc0000022 + int size = NativeMethods.GetFontData(hdc, 0, 0, null, 0); + + // Check for ntstatus.h: #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) + if ((uint)size == 0xc0000022) + throw new InvalidOperationException("Microsoft Azure returns STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) from GetFontData. This is a bug in Azure. You must implement a FontResolver to circumvent this issue."); + + if (size == NativeMethods.GDI_ERROR) + { + // Assume that the font file is a true type collection. + size = NativeMethods.GetFontData(hdc, ttcf, 0, null, 0); + isTtcf = true; + } + error = Marshal.GetLastWin32Error(); + //Debug.Assert(error == 0); + + if (size == 0) + throw new InvalidOperationException("Cannot retrieve font data."); + + byte[] bytes = new byte[size]; + int effectiveSize = NativeMethods.GetFontData(hdc, isTtcf ? ttcf : 0, 0, bytes, size); + Debug.Assert(size == effectiveSize); + // Clean up. + NativeMethods.SelectObject(hdc, oldFont); + NativeMethods.ReleaseDC(IntPtr.Zero, hdc); + + return bytes; + } +#endif + +#if WPF && !SILVERLIGHT + internal static XFontSource GetOrCreateFromWpf(string typefaceKey, WpfGlyphTypeface wpfGlyphTypeface) + { + byte[] bytes = ReadFontBytesFromWpf(wpfGlyphTypeface); + XFontSource fontSource = GetOrCreateFrom(typefaceKey, bytes); + return fontSource; + } + + internal static byte[] ReadFontBytesFromWpf(WpfGlyphTypeface wpfGlyphTypeface) + { + using (Stream fontStream = wpfGlyphTypeface.GetFontStream()) + { + if (fontStream == null) + throw new InvalidOperationException("Cannot retrieve font data."); + int size = (int)fontStream.Length; + byte[] bytes = new byte[size]; + fontStream.Read(bytes, 0, size); + return bytes; + } + } +#endif + + static XFontSource GetOrCreateFrom(string typefaceKey, byte[] fontBytes) + { + XFontSource fontSource; + ulong key = FontHelper.CalcChecksum(fontBytes); + if (FontFactory.TryGetFontSourceByKey(key, out fontSource)) + { + // The font source already exists, but is not yet cached under the specified typeface key. + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + else + { + // No font source exists. Create new one and cache it. + fontSource = new XFontSource(fontBytes, key); + FontFactory.CacheNewFontSource(typefaceKey, fontSource); + } + return fontSource; + } + + public static XFontSource CreateCompiledFont(byte[] bytes) + { + XFontSource fontSource = new XFontSource(bytes, 0); + return fontSource; + } + + /// + /// Gets or sets the fontface. + /// + internal OpenTypeFontface Fontface + { + get { return _fontface; } + set + { + _fontface = value; + _fontName = value.name.FullFontName; + } + } + OpenTypeFontface _fontface; + + /// + /// Gets the key that uniquely identifies this font source. + /// + internal ulong Key + { + get + { + if (_key == 0) + _key = FontHelper.CalcChecksum(Bytes); + return _key; + } + } + ulong _key; + + public void IncrementKey() + { + // HACK: Depends on implementation of CalcChecksum. + // Increment check sum and keep length untouched. + _key += 1ul << 32; + } + + /// + /// Gets the name of the font's name table. + /// + public string FontName + { + get { return _fontName; } + } + string _fontName; + + /// + /// Gets the bytes of the font. + /// + public byte[] Bytes + { + get { return _bytes; } + } + readonly byte[] _bytes; + + public override int GetHashCode() + { + return (int)((Key >> 32) ^ Key); + } + + public override bool Equals(object obj) + { + XFontSource fontSource = obj as XFontSource; + if (fontSource == null) + return false; + return Key == fontSource.Key; + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSha rper disable UnusedMember.Local + internal string DebuggerDisplay + // ReShar per restore UnusedMember.Local + { + // The key is converted to a value a human can remember during debugging. + get { return String.Format(CultureInfo.InvariantCulture, "XFontSource: '{0}', keyhash={1}", FontName, Key % 99991 /* largest prime number less than 100000 */); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontStretch.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontStretch.cs new file mode 100644 index 00000000..ed41f93a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontStretch.cs @@ -0,0 +1,53 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ +#if PDFSHARP20 + enum FontStretchValues + { + UltraCondensed = 1, + ExtraCondensed = 2, + Condensed = 3, + SemiCondensed = 4, + Normal = 5, + SemiExpanded = 6, + Expanded = 7, + ExtraExpanded = 8, + UltraExpanded = 9, + } + + /// + /// NYI. Reserved for future extensions of PDFsharp. + /// + // [DebuggerDisplay("'{Name}', {Size}")] + public class XFontStretch + { } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontWeight.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontWeight.cs new file mode 100644 index 00000000..a00c22e2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontWeight.cs @@ -0,0 +1,174 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif + +// Not used in PDFsharp 1.x. + +namespace PdfSharp.Drawing +{ +#if true_ // PDFSHARP20 + /// + /// Defines the density of a typeface, in terms of the lightness or heaviness of the strokes. + /// + [DebuggerDisplay("'{Weight}'")] + public class XFontWeight : IFormattable + { + internal XFontWeight(int weight) + { + _weight = weight; + } + + /// + /// Gets the weight of the font, a value between 1 and 999. + /// + public int Weight + { + get { return (_weight); } + } + private readonly int _weight; + + //public static XFontWeight FromOpenTypeWeight(int weightValue) + //{ + // if (weightValue < 1 || weightValue > 999) + // throw new ArgumentOutOfRangeException("weightValue", "Parameter must be between 1 and 999."); + // return new XFontWeight(weightValue); + //} + + /// + /// Compares the specified font weights. + /// + public static int Compare(XFontWeight left, XFontWeight right) + { + return left._weight - right._weight; + } + + /// + /// Implements the operator <. + /// + public static bool operator <(XFontWeight left, XFontWeight right) + { + return Compare(left, right) < 0; + } + + /// + /// Implements the operator <=. + /// + public static bool operator <=(XFontWeight left, XFontWeight right) + { + return Compare(left, right) <= 0; + } + + /// + /// Implements the operator >. + /// + public static bool operator >(XFontWeight left, XFontWeight right) + { + return Compare(left, right) > 0; + } + + /// + /// Implements the operator >=. + /// + public static bool operator >=(XFontWeight left, XFontWeight right) + { + return Compare(left, right) >= 0; + } + + /// + /// Implements the operator ==. + /// + public static bool operator ==(XFontWeight left, XFontWeight right) + { + return Compare(left, right) == 0; + } + + /// + /// Implements the operator !=. + /// + public static bool operator !=(XFontWeight left, XFontWeight right) + { + return !(left == right); + } + + /// + /// Determines whether the specified is equal to the current . + /// + public bool Equals(XFontWeight obj) + { + return this == obj; + } + + /// + /// Determines whether the specified is equal to the current . + /// + public override bool Equals(object obj) + { + return (obj is XFontWeight) && this == ((XFontWeight)obj); + } + + /// + /// Serves as a hash function for this type. + /// + public override int GetHashCode() + { + return Weight; + } + + /// + /// Returns a that represents the current . + /// + public override string ToString() + { + return ConvertToString(null, null); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + provider = provider ?? CultureInfo.InvariantCulture; + string str; + if (!XFontWeights.FontWeightToString(Weight, out str)) + return Weight.ToString(format, provider); + return str; + } + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XFontWeights.cs b/src/PDFsharp/src/PdfSharp/Drawing/XFontWeights.cs new file mode 100644 index 00000000..6d310f4e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XFontWeights.cs @@ -0,0 +1,309 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Not used in PDFsharp 1.x. + +namespace PdfSharp.Drawing +{ + enum FontWeightValues + { + Thin = 100, + ExtraLight = 200, + Light = 300, + Normal = 400, + Medium = 500, + SemiBold = 600, + Bold = 700, + ExtraBold = 800, + Black = 900, + ExtraBlack = 950, + } + +#if true_ // PDFSHARP20 + /// + /// Defines a set of static predefined XFontWeight values. + /// + public static class XFontWeights + { + internal static bool FontWeightStringToKnownWeight(string s, IFormatProvider provider, ref XFontWeight fontWeight) + { + int num; + switch (s.ToLower()) + { + case "thin": + fontWeight = Thin; + return true; + + case "extralight": + fontWeight = ExtraLight; + return true; + + case "ultralight": + fontWeight = UltraLight; + return true; + + case "light": + fontWeight = Light; + return true; + + case "normal": + fontWeight = Normal; + return true; + + case "regular": + fontWeight = Regular; + return true; + + case "medium": + fontWeight = Medium; + return true; + + case "semibold": + fontWeight = SemiBold; + return true; + + case "demibold": + fontWeight = DemiBold; + return true; + + case "bold": + fontWeight = Bold; + return true; + + case "extrabold": + fontWeight = ExtraBold; + return true; + + case "ultrabold": + fontWeight = UltraBold; + return true; + + case "heavy": + fontWeight = Heavy; + return true; + + case "black": + fontWeight = Black; + return true; + + case "extrablack": + fontWeight = ExtraBlack; + return true; + + case "ultrablack": + fontWeight = UltraBlack; + return true; + } + + if (Int32.TryParse(s, NumberStyles.Integer, provider, out num)) + { + fontWeight = new XFontWeight(num); + return true; + } + return false; + } + + internal static bool FontWeightToString(int weight, out string convertedValue) + { + switch (weight) + { + case 100: + convertedValue = "Thin"; + return true; + + case 200: + convertedValue = "ExtraLight"; + return true; + + case 300: + convertedValue = "Light"; + return true; + + case 400: + convertedValue = "Normal"; + return true; + + case 500: + convertedValue = "Medium"; + return true; + + case 600: + convertedValue = "SemiBold"; + return true; + + case 700: + convertedValue = "Bold"; + return true; + + case 800: + convertedValue = "ExtraBold"; + return true; + + case 900: + convertedValue = "Black"; + return true; + + case 950: + convertedValue = "ExtraBlack"; + return true; + } + convertedValue = null; + return false; + } + + /// + /// Specifies a "Thin" font weight. + /// + public static XFontWeight Thin + { + get { return new XFontWeight(100); } + } + + /// + /// Specifies a "ExtraLight" font weight. + /// + public static XFontWeight ExtraLight + { + get { return new XFontWeight(200); } + } + + /// + /// Specifies a "UltraLight" font weight. + /// + public static XFontWeight UltraLight + { + get { return new XFontWeight(200); } + } + + /// + /// Specifies a "Light" font weight. + /// + public static XFontWeight Light + { + get { return new XFontWeight(300); } + } + + /// + /// Specifies a "Normal" font weight. + /// + public static XFontWeight Normal + { + get { return new XFontWeight(400); } + } + + /// + /// Specifies a "Regular" font weight. + /// + public static XFontWeight Regular + { + get { return new XFontWeight(400); } + } + + /// + /// Specifies a "Medium" font weight. + /// + public static XFontWeight Medium + { + get { return new XFontWeight(500); } + } + + /// + /// Specifies a "SemiBold" font weight. + /// + public static XFontWeight SemiBold + { + get { return new XFontWeight(600); } + } + + /// + /// Specifies a "DemiBold" font weight. + /// + public static XFontWeight DemiBold + { + get { return new XFontWeight(600); } + } + + /// + /// Specifies a "Bold" font weight. + /// + public static XFontWeight Bold + { + get { return new XFontWeight(700); } + } + + /// + /// Specifies a "ExtraBold" font weight. + /// + public static XFontWeight ExtraBold + { + get { return new XFontWeight(800); } + } + + /// + /// Specifies a "UltraBold" font weight. + /// + public static XFontWeight UltraBold + { + get { return new XFontWeight(800); } + } + + /// + /// Specifies a "Heavy" font weight. + /// + public static XFontWeight Heavy + { + get { return new XFontWeight(900); } + } + + /// + /// Specifies a "Black" font weight. + /// + public static XFontWeight Black + { + get { return new XFontWeight(900); } + } + + /// + /// Specifies a "ExtraBlack" font weight. + /// + public static XFontWeight ExtraBlack + { + get { return new XFontWeight(950); } + } + + /// + /// Specifies a "UltraBlack" font weight. + /// + public static XFontWeight UltraBlack + { + get { return new XFontWeight(950); } + } + } +#endif +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XForm.cs b/src/PDFsharp/src/PdfSharp/Drawing/XForm.cs new file mode 100644 index 00000000..e28f5c38 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XForm.cs @@ -0,0 +1,570 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing.Pdf; +using PdfSharp.Pdf; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Filters; + +namespace PdfSharp.Drawing +{ + /// + /// Represents a graphical object that can be used to render retained graphics on it. + /// In GDI+ it is represented by a Metafile, in WPF by a DrawingVisual, and in PDF by a Form XObjects. + /// + public class XForm : XImage, IContentStream + { + internal enum FormState + { + /// + /// The form is an imported PDF page. + /// + NotATemplate, + + /// + /// The template is just created. + /// + Created, + + /// + /// XGraphics.FromForm() was called. + /// + UnderConstruction, + + /// + /// The form was drawn at least once and is 'frozen' now. + /// + Finished, + } + + /// + /// Initializes a new instance of the class. + /// + protected XForm() + { } + +#if GDI + /// + /// Initializes a new instance of the XForm class such that it can be drawn on the specified graphics + /// object. + /// + /// The graphics object that later is used to draw this form. + /// The size in points of the form. + public XForm(XGraphics gfx, XSize size) + { + if (gfx == null) + throw new ArgumentNullException("gfx"); + if (size.Width < 1 || size.Height < 1) + throw new ArgumentNullException("size", "The size of the XPdfForm is to small."); + + _formState = FormState.Created; + //templateSize = size; + _viewBox.Width = size.Width; + _viewBox.Height = size.Height; + + // If gfx belongs to a PdfPage also create the PdfFormXObject + if (gfx.PdfPage != null) + { + _document = gfx.PdfPage.Owner; + _pdfForm = new PdfFormXObject(_document, this); + PdfRectangle rect = new PdfRectangle(new XPoint(), size); + _pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect); + } + } +#endif + +#if GDI + /// + /// Initializes a new instance of the XForm class such that it can be drawn on the specified graphics + /// object. + /// + /// The graphics object that later is used to draw this form. + /// The width of the form. + /// The height of the form. + public XForm(XGraphics gfx, XUnit width, XUnit height) + : this(gfx, new XSize(width, height)) + { } +#endif + + /// + /// Initializes a new instance of the class that represents a page of a PDF document. + /// + /// The PDF document. + /// The view box of the page. + public XForm(PdfDocument document, XRect viewBox) + { + if (viewBox.Width < 1 || viewBox.Height < 1) + throw new ArgumentNullException("viewBox", "The size of the XPdfForm is to small."); + // I must tie the XPdfForm to a document immediately, because otherwise I would have no place where + // to store the resources. + if (document == null) + throw new ArgumentNullException("document", "An XPdfForm template must be associated with a document at creation time."); + + _formState = FormState.Created; + _document = document; + _pdfForm = new PdfFormXObject(document, this); + //_templateSize = size; + _viewBox = viewBox; + PdfRectangle rect = new PdfRectangle(viewBox); + _pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect); + } + + /// + /// Initializes a new instance of the class that represents a page of a PDF document. + /// + /// The PDF document. + /// The size of the page. + public XForm(PdfDocument document, XSize size) + : this(document, new XRect(0, 0, size.Width, size.Height)) + { + ////if (size.width < 1 || size.height < 1) + //// throw new ArgumentNullException("size", "The size of the XPdfForm is to small."); + ////// I must tie the XPdfForm to a document immediately, because otherwise I would have no place where + ////// to store the resources. + ////if (document == null) + //// throw new ArgumentNullException("document", "An XPdfForm template must be associated with a document."); + + ////_formState = FormState.Created; + ////_document = document; + ////pdfForm = new PdfFormXObject(document, this); + ////templateSize = size; + ////PdfRectangle rect = new PdfRectangle(new XPoint(), size); + ////pdfForm.Elements.SetRectangle(PdfFormXObject.Keys.BBox, rect); + } + + /// + /// Initializes a new instance of the class that represents a page of a PDF document. + /// + /// The PDF document. + /// The width of the page. + /// The height of the page + public XForm(PdfDocument document, XUnit width, XUnit height) + : this(document, new XRect(0, 0, width, height)) + { } + + /// + /// This function should be called when drawing the content of this form is finished. + /// The XGraphics object used for drawing the content is disposed by this function and + /// cannot be used for any further drawing operations. + /// PDFsharp automatically calls this function when this form was used the first time + /// in a DrawImage function. + /// + public void DrawingFinished() + { + if (_formState == FormState.Finished) + return; + + if (_formState == FormState.NotATemplate) + throw new InvalidOperationException("This object is an imported PDF page and you cannot finish drawing on it because you must not draw on it at all."); + + Finish(); + } + + /// + /// Called from XGraphics constructor that creates an instance that work on this form. + /// + internal void AssociateGraphics(XGraphics gfx) + { + if (_formState == FormState.NotATemplate) + throw new NotImplementedException("The current version of PDFsharp cannot draw on an imported page."); + + if (_formState == FormState.UnderConstruction) + throw new InvalidOperationException("An XGraphics object already exists for this form."); + + if (_formState == FormState.Finished) + throw new InvalidOperationException("After drawing a form it cannot be modified anymore."); + + Debug.Assert(_formState == FormState.Created); + _formState = FormState.UnderConstruction; + Gfx = gfx; + } + internal XGraphics Gfx; + + /// + /// Disposes this instance. + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + /// + /// Sets the form in the state FormState.Finished. + /// + internal virtual void Finish() + { +#if GDI + if (_formState == FormState.NotATemplate || _formState == FormState.Finished) + return; + + if (Gfx.Metafile != null) + _gdiImage = Gfx.Metafile; + + Debug.Assert(_formState == FormState.Created || _formState == FormState.UnderConstruction); + _formState = FormState.Finished; + Gfx.Dispose(); + Gfx = null; + + if (PdfRenderer != null) + { + //pdfForm.CreateStream(PdfEncoders.RawEncoding.GetBytes(PdfRenderer.GetContent())); + PdfRenderer.Close(); + Debug.Assert(PdfRenderer == null); + + if (_document.Options.CompressContentStreams) + { + _pdfForm.Stream.Value = Filtering.FlateDecode.Encode(_pdfForm.Stream.Value, _document.Options.FlateEncodeMode); + _pdfForm.Elements["/Filter"] = new PdfName("/FlateDecode"); + } + int length = _pdfForm.Stream.Length; + _pdfForm.Elements.SetInteger("/Length", length); + } +#endif +#if WPF +#endif + } + + /// + /// Gets the owning document. + /// + internal PdfDocument Owner + { + get { return _document; } + } + PdfDocument _document; + + /// + /// Gets the color model used in the underlying PDF document. + /// + internal PdfColorMode ColorMode + { + get + { + if (_document == null) + return PdfColorMode.Undefined; + return _document.Options.ColorMode; + } + } + + /// + /// Gets a value indicating whether this instance is a template. + /// + internal bool IsTemplate + { + get { return _formState != FormState.NotATemplate; } + } + internal FormState _formState; + + /// + /// Get the width of the page identified by the property PageNumber. + /// + [Obsolete("Use either PixelWidth or PointWidth. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelWidth, but will become PointWidth in future releases of PDFsharp.")] + public override double Width + { + //get { return templateSize.width; } + get { return _viewBox.Width; } + } + + /// + /// Get the width of the page identified by the property PageNumber. + /// + [Obsolete("Use either PixelHeight or PointHeight. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelHeight, but will become PointHeight in future releases of PDFsharp.")] + public override double Height + { + //get { return templateSize.height; } + get { return _viewBox.Height; } + } + + /// + /// Get the width in point of this image. + /// + public override double PointWidth + { + //get { return templateSize.width; } + get { return _viewBox.Width; } + } + + /// + /// Get the height in point of this image. + /// + public override double PointHeight + { + //get { return templateSize.height; } + get { return _viewBox.Height; } + } + + /// + /// Get the width of the page identified by the property PageNumber. + /// + public override int PixelWidth + { + //get { return (int)templateSize.width; } + get { return (int)_viewBox.Width; } + } + + /// + /// Get the height of the page identified by the property PageNumber. + /// + public override int PixelHeight + { + //get { return (int)templateSize.height; } + get { return (int)_viewBox.Height; } + } + + /// + /// Get the size of the page identified by the property PageNumber. + /// + public override XSize Size + { + //get { return templateSize; } + get { return _viewBox.Size; } + } + //XSize templateSize; + + /// + /// Gets the view box of the form. + /// + public XRect ViewBox + { + get { return _viewBox; } + } + XRect _viewBox; + + /// + /// Gets 72, the horizontal resolution by design of a form object. + /// + public override double HorizontalResolution + { + get { return 72; } + } + + /// + /// Gets 72 always, the vertical resolution by design of a form object. + /// + public override double VerticalResolution + { + get { return 72; } + } + + /// + /// Gets or sets the bounding box. + /// + public XRect BoundingBox + { + get { return _boundingBox; } + set { _boundingBox = value; } // TODO: pdfForm = null + } + XRect _boundingBox; + + /// + /// Gets or sets the transformation matrix. + /// + public virtual XMatrix Transform + { + get { return _transform; } + set + { + if (_formState == FormState.Finished) + throw new InvalidOperationException("After a XPdfForm was once drawn it must not be modified."); + _transform = value; + } + } + internal XMatrix _transform; + + internal PdfResources Resources + { + get + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + return PdfForm.Resources; + //if (resources == null) + // resources = (PdfResources)pdfForm.Elements.GetValue(PdfFormXObject.Keys.Resources, VCF.Create); // VCF.CreateIndirect + //return resources; + } + } + //PdfResources resources; + + /// + /// Implements the interface because the primary function is internal. + /// + PdfResources IContentStream.Resources + { + get { return Resources; } + } + + /// + /// Gets the resource name of the specified font within this form. + /// + internal string GetFontName(XFont font, out PdfFont pdfFont) + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + pdfFont = _document.FontTable.GetFont(font); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(XFont font, out PdfFont pdfFont) + { + return GetFontName(font, out pdfFont); + } + + /// + /// Tries to get the resource name of the specified font data within this form. + /// Returns null if no such font exists. + /// + internal string TryGetFontName(string idName, out PdfFont pdfFont) + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + pdfFont = _document.FontTable.TryGetFont(idName); + string name = null; + if (pdfFont != null) + name = Resources.AddFont(pdfFont); + return name; + } + + /// + /// Gets the resource name of the specified font data within this form. + /// + internal string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + pdfFont = _document.FontTable.GetFont(idName, fontData); + //pdfFont = new PdfType0Font(Owner, idName, fontData); + //pdfFont.Document = _document; + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + return GetFontName(idName, fontData, out pdfFont); + } + + /// + /// Gets the resource name of the specified image within this form. + /// + internal string GetImageName(XImage image) + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + PdfImage pdfImage = _document.ImageTable.GetImage(image); + Debug.Assert(pdfImage != null); + string name = Resources.AddImage(pdfImage); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetImageName(XImage image) + { + return GetImageName(image); + } + + internal PdfFormXObject PdfForm + { + get + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + if (_pdfForm.Reference == null) + _document._irefTable.Add(_pdfForm); + return _pdfForm; + } + } + + /// + /// Gets the resource name of the specified form within this form. + /// + internal string GetFormName(XForm form) + { + Debug.Assert(IsTemplate, "This function is for form templates only."); + PdfFormXObject pdfForm = _document.FormTable.GetForm(form); + Debug.Assert(pdfForm != null); + string name = Resources.AddForm(pdfForm); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetFormName(XForm form) + { + return GetFormName(form); + } + + /// + /// The PdfFormXObject gets invalid when PageNumber or transform changed. This is because a modification + /// of an XPdfForm must not change objects that are already been drawn. + /// + internal PdfFormXObject _pdfForm; // TODO: make private + + internal XGraphicsPdfRenderer PdfRenderer; + +#if WPF && !SILVERLIGHT + /// + /// Gets a value indicating whether this image is cmyk. + /// + /// true if this image is cmyk; otherwise, false. + internal override bool IsCmyk + { + get { return false; } // not supported and not relevant + } + + /// + /// Gets a value indicating whether this image is JPEG. + /// + /// true if this image is JPEG; otherwise, false. + internal override bool IsJpeg + { + get { return base.IsJpeg; }// not supported and not relevant + } + + /// + /// Gets the JPEG memory stream (if IsJpeg returns true). + /// + /// The memory. + public override MemoryStream Memory + { + get { throw new NotImplementedException(); } + } +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs new file mode 100644 index 00000000..695536bd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGlyphTypeface.cs @@ -0,0 +1,602 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +#if CORE || GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +using WpfStyleSimulations = System.Windows.Media.StyleSimulations; +#endif +#if UWP +using Windows.UI.Xaml.Media; +#endif +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; + +#pragma warning disable 649 +#if SILVERLIGHT +#pragma warning disable 219 +#endif +#if NETFX_CORE +#pragma warning disable 649 +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Specifies a physical font face that corresponds to a font file on the disk or in memory. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal sealed class XGlyphTypeface + { + // Implementation Notes + // XGlyphTypeface is the centerpiece for font management. There is a one to one relationship + // between XFont an XGlyphTypeface. + // + // * Each XGlyphTypeface can belong to one or more XFont objects. + // * An XGlyphTypeface hold an XFontFamily. + // * XGlyphTypeface hold a reference to an OpenTypeFontface. + // * + // + + const string KeyPrefix = "tk:"; // "typeface key" + +#if CORE || GDI + XGlyphTypeface(string key, XFontFamily fontFamily, XFontSource fontSource, XStyleSimulations styleSimulations, GdiFont gdiFont) + { + _key = key; + _fontFamily = fontFamily; + _fontSource = fontSource; + + _fontface = OpenTypeFontface.CetOrCreateFrom(fontSource); + Debug.Assert(ReferenceEquals(_fontSource.Fontface, _fontface)); + + _gdiFont = gdiFont; + + _styleSimulations = styleSimulations; + Initialize(); + } +#endif + +#if GDI + /// + /// Initializes a new instance of the class by a font source. + /// + public XGlyphTypeface(XFontSource fontSource) + { + string familyName = fontSource.Fontface.name.Name; + _fontFamily = new XFontFamily(familyName, false); + _fontface = fontSource.Fontface; + _isBold = _fontface.os2.IsBold; + _isItalic = _fontface.os2.IsItalic; + + _key = ComputeKey(familyName, _isBold, _isItalic); + //_fontFamily =xfont FontFamilyCache.GetFamilyByName(familyName); + _fontSource = fontSource; + + Initialize(); + } +#endif + +#if WPF + XGlyphTypeface(string key, XFontFamily fontFamily, XFontSource fontSource, XStyleSimulations styleSimulations, WpfTypeface wpfTypeface, WpfGlyphTypeface wpfGlyphTypeface) + { + _key = key; + _fontFamily = fontFamily; + _fontSource = fontSource; + _styleSimulations = styleSimulations; + + _fontface = OpenTypeFontface.CetOrCreateFrom(fontSource); + Debug.Assert(ReferenceEquals(_fontSource.Fontface, _fontface)); + + _wpfTypeface = wpfTypeface; + _wpfGlyphTypeface = wpfGlyphTypeface; + + Initialize(); + } +#endif + +#if NETFX_CORE || UWP || DNC10 + XGlyphTypeface(string key, XFontFamily fontFamily, XFontSource fontSource, XStyleSimulations styleSimulations) + { + _key = key; + _fontFamily = fontFamily; + _fontSource = fontSource; + _styleSimulations = styleSimulations; + + _fontface = OpenTypeFontface.CetOrCreateFrom(fontSource); + Debug.Assert(ReferenceEquals(_fontSource.Fontface, _fontface)); + + //_wpfTypeface = wpfTypeface; + //_wpfGlyphTypeface = wpfGlyphTypeface; + + Initialize(); + } +#endif + + public static XGlyphTypeface GetOrCreateFrom(string familyName, FontResolvingOptions fontResolvingOptions) + { + // Check cache for requested type face. + string typefaceKey = ComputeKey(familyName, fontResolvingOptions); + XGlyphTypeface glyphTypeface; + try + { + // Lock around TryGetGlyphTypeface and AddGlyphTypeface. + Lock.EnterFontFactory(); + if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) + { + // Just return existing one. + return glyphTypeface; + } + + // Resolve typeface by FontFactory. + FontResolverInfo fontResolverInfo = FontFactory.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey); + if (fontResolverInfo == null) + { + // No fallback - just stop. + throw new InvalidOperationException("No appropriate font found."); + } + +#if CORE || GDI + GdiFont gdiFont = null; +#endif +#if WPF + WpfFontFamily wpfFontFamily = null; + WpfTypeface wpfTypeface = null; + WpfGlyphTypeface wpfGlyphTypeface = null; +#endif +#if UWP + // Nothing to do. +#endif + // Now create the font family at the first. + XFontFamily fontFamily; + PlatformFontResolverInfo platformFontResolverInfo = fontResolverInfo as PlatformFontResolverInfo; + if (platformFontResolverInfo != null) + { + // Case: fontResolverInfo was created by platform font resolver + // and contains platform specific objects that are reused. +#if CORE || GDI + // Reuse GDI+ font from platform font resolver. + gdiFont = platformFontResolverInfo.GdiFont; + fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); +#endif +#if WPF +#if !SILVERLIGHT + // Reuse WPF font family created from platform font resolver. + wpfFontFamily = platformFontResolverInfo.WpfFontFamily; + wpfTypeface = platformFontResolverInfo.WpfTypeface; + wpfGlyphTypeface = platformFontResolverInfo.WpfGlyphTypeface; + fontFamily = XFontFamily.GetOrCreateFromWpf(wpfFontFamily); +#else + fontFamily = XFontFamily.GetOrCreateFromWpf(new WpfFontFamily(familyName)); +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + fontFamily = null; +#endif + } + else + { + // Case: fontResolverInfo was created by custom font resolver. + + // Get or create font family for custom font resolver retrieved font source. + fontFamily = XFontFamily.GetOrCreateFontFamily(familyName); + } + + // We have a valid font resolver info. That means we also have an XFontSource object loaded in the cache. + XFontSource fontSource = FontFactory.GetFontSourceByFontName(fontResolverInfo.FaceName); + Debug.Assert(fontSource != null); + + // Each font source already contains its OpenTypeFontface. +#if CORE || GDI + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, gdiFont); +#endif +#if WPF + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations, wpfTypeface, wpfGlyphTypeface); +#endif +#if NETFX_CORE || UWP || DNC10 + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, fontResolverInfo.StyleSimulations); +#endif + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); + } + finally { Lock.ExitFontFactory(); } + return glyphTypeface; + } + +#if CORE || GDI + public static XGlyphTypeface GetOrCreateFromGdi(GdiFont gdiFont) + { + // $TODO THHO Lock??? + string typefaceKey = ComputeKey(gdiFont); + XGlyphTypeface glyphTypeface; + if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) + { + // We have the glyph typeface already in cache. + return glyphTypeface; + } + + XFontFamily fontFamily = XFontFamily.GetOrCreateFromGdi(gdiFont); + XFontSource fontSource = XFontSource.GetOrCreateFromGdi(typefaceKey, gdiFont); + + // Check if styles must be simulated. + XStyleSimulations styleSimulations = XStyleSimulations.None; + if (gdiFont.Bold && !fontSource.Fontface.os2.IsBold) + styleSimulations |= XStyleSimulations.BoldSimulation; + if (gdiFont.Italic && !fontSource.Fontface.os2.IsItalic) + styleSimulations |= XStyleSimulations.ItalicSimulation; + + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, styleSimulations, gdiFont); + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); + + return glyphTypeface; + } +#endif + +#if WPF && !SILVERLIGHT + public static XGlyphTypeface GetOrCreateFromWpf(WpfTypeface wpfTypeface) + { +#if DEBUG + if (wpfTypeface.FontFamily.Source == "Segoe UI Semilight") + wpfTypeface.GetType(); +#endif + //string typefaceKey = ComputeKey(wpfTypeface); + //XGlyphTypeface glyphTypeface; + //if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) + //{ + // // We have the glyph typeface already in cache. + // return glyphTypeface; + //} + + // Lock around TryGetGlyphTypeface and AddGlyphTypeface. + try + { + Lock.EnterFontFactory(); + + // Create WPF glyph typeface. + WpfGlyphTypeface wpfGlyphTypeface; + if (!wpfTypeface.TryGetGlyphTypeface(out wpfGlyphTypeface)) + return null; + + string typefaceKey = ComputeKey(wpfGlyphTypeface); + + string name1 = wpfGlyphTypeface.DesignerNames[FontHelper.CultureInfoEnUs]; + string name2 = wpfGlyphTypeface.FaceNames[FontHelper.CultureInfoEnUs]; + string name3 = wpfGlyphTypeface.FamilyNames[FontHelper.CultureInfoEnUs]; + string name4 = wpfGlyphTypeface.ManufacturerNames[FontHelper.CultureInfoEnUs]; + string name5 = wpfGlyphTypeface.Win32FaceNames[FontHelper.CultureInfoEnUs]; + string name6 = wpfGlyphTypeface.Win32FamilyNames[FontHelper.CultureInfoEnUs]; + + XGlyphTypeface glyphTypeface; + if (GlyphTypefaceCache.TryGetGlyphTypeface(typefaceKey, out glyphTypeface)) + { + // We have the glyph typeface already in cache. + return glyphTypeface; + } + + XFontFamily fontFamily = XFontFamily.GetOrCreateFromWpf(wpfTypeface.FontFamily); + XFontSource fontSource = XFontSource.GetOrCreateFromWpf(typefaceKey, wpfGlyphTypeface); + + glyphTypeface = new XGlyphTypeface(typefaceKey, fontFamily, fontSource, + (XStyleSimulations)wpfGlyphTypeface.StyleSimulations, + wpfTypeface, wpfGlyphTypeface); + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); + + return glyphTypeface; + } + finally { Lock.ExitFontFactory(); } + } +#endif + + public XFontFamily FontFamily + { + get { return _fontFamily; } + } + readonly XFontFamily _fontFamily; + + internal OpenTypeFontface Fontface + { + get { return _fontface; } + } + readonly OpenTypeFontface _fontface; + + public XFontSource FontSource + { + get { return _fontSource; } + } + readonly XFontSource _fontSource; + + void Initialize() + { + _familyName = _fontface.name.Name; + if (string.IsNullOrEmpty(_faceName) || _faceName.StartsWith("?")) + _faceName = _familyName; + _styleName = _fontface.name.Style; + _displayName = _fontface.name.FullFontName; + if (string.IsNullOrEmpty(_displayName)) + { + _displayName = _familyName; + if (string.IsNullOrEmpty(_styleName)) + _displayName += " (" + _styleName + ")"; + } + + // Bold, as defined in OS/2 table. + _isBold = _fontface.os2.IsBold; + // Debug.Assert(_isBold == (_fontface.os2.usWeightClass > 400), "Check font weight."); + + // Italic, as defined in OS/2 table. + _isItalic = _fontface.os2.IsItalic; + } + + /// + /// Gets the name of the font face. This can be a file name, an uri, or a GUID. + /// + internal string FaceName + { + get { return _faceName; } + } + string _faceName; + + /// + /// Gets the English family name of the font, for example "Arial". + /// + public string FamilyName + { + get { return _familyName; } + } + string _familyName; + + /// + /// Gets the English subfamily name of the font, + /// for example "Bold". + /// + public string StyleName + { + get { return _styleName; } + } + string _styleName; + + /// + /// Gets the English display name of the font, + /// for example "Arial italic". + /// + public string DisplayName + { + get { return _displayName; } + } + string _displayName; + + /// + /// Gets a value indicating whether the font weight is bold. + /// + public bool IsBold + { + get { return _isBold; } + } + bool _isBold; + + /// + /// Gets a value indicating whether the font style is italic. + /// + public bool IsItalic + { + get { return _isItalic; } + } + bool _isItalic; + + public XStyleSimulations StyleSimulations + { + get { return _styleSimulations; } + } + XStyleSimulations _styleSimulations; + + /// + /// Gets the suffix of the face name in a PDF font and font descriptor. + /// The name based on the effective value of bold and italic from the OS/2 table. + /// + string GetFaceNameSuffix() + { + // Use naming of Microsoft Word. + if (IsBold) + return IsItalic ? ",BoldItalic" : ",Bold"; + return IsItalic ? ",Italic" : ""; + } + + internal string GetBaseName() + { + string name = DisplayName; + int ich = name.IndexOf("bold", StringComparison.OrdinalIgnoreCase); + if (ich > 0) + name = name.Substring(0, ich) + name.Substring(ich + 4, name.Length - ich - 4); + ich = name.IndexOf("italic", StringComparison.OrdinalIgnoreCase); + if (ich > 0) + name = name.Substring(0, ich) + name.Substring(ich + 6, name.Length - ich - 6); + //name = name.Replace(" ", ""); + name = name.Trim(); + name += GetFaceNameSuffix(); + return name; + } + + /// + /// Computes the bijective key for a typeface. + /// + internal static string ComputeKey(string familyName, FontResolvingOptions fontResolvingOptions) + { + // Compute a human readable key. + string simulationSuffix = ""; + if (fontResolvingOptions.OverrideStyleSimulations) + { + switch (fontResolvingOptions.StyleSimulations) + { + case XStyleSimulations.BoldSimulation: simulationSuffix = "|b+/i-"; break; + case XStyleSimulations.ItalicSimulation: simulationSuffix = "|b-/i+"; break; + case XStyleSimulations.BoldItalicSimulation: simulationSuffix = "|b+/i+"; break; + case XStyleSimulations.None: break; + default: throw new ArgumentOutOfRangeException("fontResolvingOptions"); + } + } + string key = KeyPrefix + familyName.ToLowerInvariant() + + (fontResolvingOptions.IsItalic ? "/i" : "/n") // normal / oblique / italic + + (fontResolvingOptions.IsBold ? "/700" : "/400") + "/5" // Stretch.Normal + + simulationSuffix; + return key; + } + + /// + /// Computes the bijective key for a typeface. + /// + internal static string ComputeKey(string familyName, bool isBold, bool isItalic) + { + return ComputeKey(familyName, new FontResolvingOptions(FontHelper.CreateStyle(isBold, isItalic))); + } + +#if CORE || GDI + internal static string ComputeKey(GdiFont gdiFont) + { + string name1 = gdiFont.Name; + string name2 = gdiFont.OriginalFontName; + string name3 = gdiFont.SystemFontName; + + string name = name1; + GdiFontStyle style = gdiFont.Style; + + string key = KeyPrefix + name.ToLowerInvariant() + ((style & GdiFontStyle.Italic) == GdiFontStyle.Italic ? "/i" : "/n") + ((style & GdiFontStyle.Bold) == GdiFontStyle.Bold ? "/700" : "/400") + "/5"; // Stretch.Normal + return key; + } +#endif +#if WPF && !SILVERLIGHT + internal static string ComputeKey(WpfGlyphTypeface wpfGlyphTypeface) + { + string name1 = wpfGlyphTypeface.DesignerNames[FontHelper.CultureInfoEnUs]; + string faceName = wpfGlyphTypeface.FaceNames[FontHelper.CultureInfoEnUs]; + string familyName = wpfGlyphTypeface.FamilyNames[FontHelper.CultureInfoEnUs]; + string name4 = wpfGlyphTypeface.ManufacturerNames[FontHelper.CultureInfoEnUs]; + string name5 = wpfGlyphTypeface.Win32FaceNames[FontHelper.CultureInfoEnUs]; + string name6 = wpfGlyphTypeface.Win32FamilyNames[FontHelper.CultureInfoEnUs]; + + + string name = familyName.ToLower() + '/' + faceName.ToLowerInvariant(); + string style = wpfGlyphTypeface.Style.ToString(); + string weight = wpfGlyphTypeface.Weight.ToString(); + string stretch = wpfGlyphTypeface.Stretch.ToString(); + string simulations = wpfGlyphTypeface.StyleSimulations.ToString(); + + //string key = name + '/' + style + '/' + weight + '/' + stretch + '/' + simulations; + + string key = KeyPrefix + name + '/' + style.Substring(0, 1) + '/' + wpfGlyphTypeface.Weight.ToOpenTypeWeight().ToString(CultureInfo.InvariantCulture) + '/' + wpfGlyphTypeface.Stretch.ToOpenTypeStretch().ToString(CultureInfo.InvariantCulture); + switch (wpfGlyphTypeface.StyleSimulations) + { + case WpfStyleSimulations.BoldSimulation: key += "|b+/i-"; break; + case WpfStyleSimulations.ItalicSimulation: key += "|b-/i+"; break; + case WpfStyleSimulations.BoldItalicSimulation: key += "|b+/i+"; break; + case WpfStyleSimulations.None: break; + } + return key.ToLowerInvariant(); + } +#endif + + public string Key + { + get { return _key; } + } + readonly string _key; + +#if CORE || GDI + internal GdiFont GdiFont + { + get { return _gdiFont; } + } + + private readonly GdiFont _gdiFont; +#endif + +#if WPF + internal WpfTypeface WpfTypeface + { + get { return _wpfTypeface; } + } + readonly WpfTypeface _wpfTypeface; + + internal WpfGlyphTypeface WpfGlyphTypeface + { + get { return _wpfGlyphTypeface; } + } + readonly WpfGlyphTypeface _wpfGlyphTypeface; +#endif + +#if SILVERLIGHT_ + /// + /// Gets the FontSource object used in Silverlight 4. + /// + public FontSource FontSource + { + get + { + if (_fontSource == null) + { +#if true + MemoryStream stream = new MemoryStream(_fontface.FontData.Bytes); + _fontSource = new FontSource(stream); +#else + using (MemoryStream stream = new MemoryStream(_fontface.Data)) + { + _fontSource = new FontSource(stream); + } +#endif + } + return _fontSource; + } + } + FontSource _fontSource; +#endif + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + internal string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get { return string.Format(CultureInfo.InvariantCulture, "{0} - {1} ({2})", FamilyName, StyleName, FaceName); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGradientBrush.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGradientBrush.cs new file mode 100644 index 00000000..8fbfc8c9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGradientBrush.cs @@ -0,0 +1,80 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; +using PdfSharp.Internal; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiLinearGradientBrush =System.Drawing.Drawing2D.LinearGradientBrush; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +using WpfBrush = System.Windows.Media.Brush; +#endif +#if UWP +using Windows.UI; +using Windows.UI.Xaml.Media; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Defines a Brush with a linear gradient. + /// + public abstract class XGradientBrush : XBrush + { + /// + /// Gets or sets a value indicating whether or not to extend the gradient beyond its bounds. + /// + public bool ExtendLeft + { + get { return _extendLeft; } + set { _extendLeft = value; } + } + internal bool _extendLeft; + + /// + /// Gets or sets a value indicating whether or not to extend the gradient beyond its bounds. + /// + public bool ExtendRight + { + get { return _extendRight; } + set { _extendRight = value; } + } + internal bool _extendRight; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphics.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphics.cs new file mode 100644 index 00000000..fcab6c16 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphics.cs @@ -0,0 +1,5370 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using GdiPoint = System.Drawing.Point; +using GdiSize = System.Drawing.Size; +using GdiRect = System.Drawing.Rectangle; +using GdiPointF = System.Drawing.PointF; +using GdiSizeF = System.Drawing.SizeF; +using GdiRectF = System.Drawing.RectangleF; +using GdiMatrix = System.Drawing.Drawing2D.Matrix; +#endif +#if WPF +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using PdfSharp.Windows; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +using SysMatrix = System.Windows.Media.Matrix; +using WpfBrush = System.Windows.Media.Brush; +using WpfPen = System.Windows.Media.Pen; +#if !SILVERLIGHT +using WpfBrushes = System.Windows.Media.Brushes; +#endif +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +using SysRect = Windows.Foundation.Rect; +#endif +#if UWP +using System.Numerics; +using Windows.UI; +using Windows.UI.Xaml.Controls; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Geometry; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +using SysRect = Windows.Foundation.Rect; +#endif +using PdfSharp.Pdf; +using PdfSharp.Drawing.Pdf; +using PdfSharp.Events; +using PdfSharp.Internal; +using PdfSharp.Pdf.Advanced; + +#pragma warning disable 1587 +// ReSharper disable UseNullPropagation +// ReSharper disable RedundantNameQualifier +// ReSharper disable UseNameofExpression + +namespace PdfSharp.Drawing // #??? Clean up +{ + /// + /// Holds information about the current state of the XGraphics object. + /// + [Flags] + enum InternalGraphicsMode + { + DrawingGdiGraphics, + DrawingPdfContent, + DrawingBitmap, + } + + /// + /// Represents a drawing surface for a fixed size page. + /// + public sealed class XGraphics : IDisposable + { +#if CORE + // TODO: Implement better concept of a measure context. +#endif + +#if GDI + /// + /// Initializes a new instance of the XGraphics class. + /// + /// The gfx. + /// The size. + /// The page unit. + /// The page direction. + XGraphics(Graphics gfx, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { + if (gfx == null) + { + // MigraDoc comes here when creating a MeasureContext. + try + { + Lock.EnterGdiPlus(); + gfx = Graphics.FromHwnd(IntPtr.Zero); // BUG: Use measure image + } + finally { Lock.ExitGdiPlus(); } + } + + _gsStack = new GraphicsStateStack(this); + TargetContext = XGraphicTargetContext.GDI; + _gfx = gfx; + _drawGraphics = true; + _pageSize = new XSize(size.Width, size.Height); + _pageUnit = pageUnit; + switch (pageUnit) + { + case XGraphicsUnit.Point: + _pageSizePoints = new XSize(size.Width, size.Height); + break; + + case XGraphicsUnit.Inch: + _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height)); + break; + + case XGraphicsUnit.Millimeter: + _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height)); + break; + + case XGraphicsUnit.Centimeter: + _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height)); + break; + + case XGraphicsUnit.Presentation: + _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height)); + break; + + default: + throw new NotImplementedException("unit"); + } + + _pageDirection = pageDirection; + Initialize(); + } +#endif + +#if WPF && !SILVERLIGHT + /// + /// Initializes a new instance of the XGraphics class. + /// + /// The drawing context. + /// The size. + /// The page unit. + /// The page direction. + XGraphics(DrawingContext dc, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { + if (dc == null) + { + //throw new ArgumentNullException("dc"); + _dv = new DrawingVisual(); + dc = _dv.RenderOpen(); + } + + _gsStack = new GraphicsStateStack(this); + TargetContext = XGraphicTargetContext.WPF; + _dc = dc; + _drawGraphics = true; + _pageSize = new XSize(size.Width, size.Height); + _pageUnit = pageUnit; + switch (pageUnit) + { + case XGraphicsUnit.Point: + _pageSizePoints = new XSize(size.Width, size.Height); + break; + + case XGraphicsUnit.Inch: + _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height)); + break; + + case XGraphicsUnit.Millimeter: + _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height)); + break; + + case XGraphicsUnit.Centimeter: + _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height)); + break; + + case XGraphicsUnit.Presentation: + _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height)); + break; + + default: + throw new NotImplementedException("unit"); + } + + _pageDirection = pageDirection; + Initialize(); + } +#endif + +#if WPF + /// + /// Initializes a new instance of the XGraphics class. + /// + /// The canvas. + /// The size. + /// The page unit. + /// The page direction. + XGraphics(Canvas canvas, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { + //throw new ArgumentNullException("canvas"); + if (canvas == null) + canvas = new Canvas(); + +#if !SILVERLIGHT + // Create DrawingVisual as container for the content of the page. + _dv = new DrawingVisual(); + // Create a host that shows the visual. + VisualPresenter vp = new VisualPresenter(); + vp.Children.Add(_dv); + // The canvas only contains the host of the DrawingVisual. + canvas.Children.Add(vp); + _dc = _dv.RenderOpen(); + TargetContext = XGraphicTargetContext.WPF; + //////VisualBrush brush = new VisualBrush(_dv); + ////////brush.ViewboxUnits = BrushMappingMode. + //////brush.Viewport=new Rect(new Point(), size.ToSize()); + //////brush.Viewbox=new Rect(new Point(), size.ToSize()); + ////////brush.Viewport=new Rect(new Point(), (Size)size); + //////brush.AutoLayoutContent = true; + //////canvas.Background = brush; +#else + _dc = new AgDrawingContext(canvas); +#endif + + _gsStack = new GraphicsStateStack(this); + TargetContext = XGraphicTargetContext.WPF; + + _drawGraphics = true; + _pageSize = new XSize(size.Width, size.Height); + _pageUnit = pageUnit; + switch (pageUnit) + { + case XGraphicsUnit.Point: + _pageSizePoints = new XSize(size.Width, size.Height); + break; + + case XGraphicsUnit.Inch: + _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height)); + break; + + case XGraphicsUnit.Millimeter: + _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height)); + break; + + case XGraphicsUnit.Centimeter: + _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height)); + break; + + case XGraphicsUnit.Presentation: + _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height)); + break; + + default: + throw new NotImplementedException("unit"); + } + + _pageDirection = pageDirection; + Initialize(); + } +#endif + +#if UWP + /// + /// Initializes a new instance of the XGraphics class. + /// + /// The canvas. + /// The size. + /// The page unit. + /// The page direction. + XGraphics(CanvasDrawingSession canvasDrawingSession, XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { + if (canvasDrawingSession == null) + throw new ArgumentNullException("canvasDrawingSession"); + + _cds = canvasDrawingSession; + + _gsStack = new GraphicsStateStack(this); + TargetContext = XGraphicTargetContext.WPF; + + _drawGraphics = true; + _pageSize = new XSize(size.Width, size.Height); + _pageUnit = pageUnit; + switch (pageUnit) + { + case XGraphicsUnit.Point: + _pageSizePoints = new XSize(size.Width, size.Height); + break; + + case XGraphicsUnit.Inch: + _pageSizePoints = new XSize(XUnit.FromInch(size.Width), XUnit.FromInch(size.Height)); + break; + + case XGraphicsUnit.Millimeter: + _pageSizePoints = new XSize(XUnit.FromMillimeter(size.Width), XUnit.FromMillimeter(size.Height)); + break; + + case XGraphicsUnit.Centimeter: + _pageSizePoints = new XSize(XUnit.FromCentimeter(size.Width), XUnit.FromCentimeter(size.Height)); + break; + + case XGraphicsUnit.Presentation: + _pageSizePoints = new XSize(XUnit.FromPresentation(size.Width), XUnit.FromPresentation(size.Height)); + break; + + default: + throw new NotImplementedException("unit"); + } + + _pageDirection = pageDirection; + Initialize(); + } +#endif + + /// + /// Initializes a new instance of the XGraphics class for drawing on a PDF page. + /// + XGraphics(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { + if (page == null) + throw new ArgumentNullException("page"); + + if (page.Owner == null) + throw new ArgumentException("You cannot draw on a page that is not owned by a PdfDocument object.", "page"); + + if (page.RenderContent != null) + throw new InvalidOperationException("An XGraphics object already exists for this page and must be disposed before a new one can be created."); + + if (page.Owner.IsReadOnly) + throw new InvalidOperationException("Cannot create XGraphics for a page of a document that cannot be modified. Use PdfDocumentOpenMode.Modify."); + + _gsStack = new GraphicsStateStack(this); + PdfContent content = null; + switch (options) + { + case XGraphicsPdfPageOptions.Replace: + page.Contents.Elements.Clear(); + goto case XGraphicsPdfPageOptions.Append; + + case XGraphicsPdfPageOptions.Prepend: + content = page.Contents.PrependContent(); + break; + + case XGraphicsPdfPageOptions.Append: + content = page.Contents.AppendContent(); + break; + } + page.RenderContent = content; + +#if CORE + TargetContext = XGraphicTargetContext.CORE; +#endif +#if GDI + // HACK: This does not work with #MediumTrust + //_gfx = Graphics.FromHwnd(IntPtr.Zero); // _gfx should not be necessary anymore. + _gfx = null; + TargetContext = XGraphicTargetContext.GDI; +#endif +#if WPF && !SILVERLIGHT + _dv = new DrawingVisual(); + _dc = _dv.RenderOpen(); + TargetContext = XGraphicTargetContext.WPF; +#endif +#if SILVERLIGHT + _dc = new AgDrawingContext(new Canvas()); + TargetContext = XGraphicTargetContext.WPF; +#endif +#if GDI && WPF + TargetContext = PdfSharp.Internal.TargetContextHelper.TargetContext; +#endif + _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(page, this, options); + _pageSizePoints = new XSize(page.Width, page.Height); + switch (pageUnit) + { + case XGraphicsUnit.Point: + _pageSize = new XSize(page.Width, page.Height); + break; + + case XGraphicsUnit.Inch: + _pageSize = new XSize(XUnit.FromPoint(page.Width).Inch, XUnit.FromPoint(page.Height).Inch); + break; + + case XGraphicsUnit.Millimeter: + _pageSize = new XSize(XUnit.FromPoint(page.Width).Millimeter, XUnit.FromPoint(page.Height).Millimeter); + break; + + case XGraphicsUnit.Centimeter: + _pageSize = new XSize(XUnit.FromPoint(page.Width).Centimeter, XUnit.FromPoint(page.Height).Centimeter); + break; + + case XGraphicsUnit.Presentation: + _pageSize = new XSize(XUnit.FromPoint(page.Width).Presentation, XUnit.FromPoint(page.Height).Presentation); + break; + + default: + throw new NotImplementedException("unit"); + } + _pageUnit = pageUnit; + _pageDirection = pageDirection; + + Initialize(); + } + + /// + /// Initializes a new instance of the XGraphics class used for drawing on a form. + /// + XGraphics(XForm form) + { + if (form == null) + throw new ArgumentNullException("form"); + + _form = form; + form.AssociateGraphics(this); + + _gsStack = new GraphicsStateStack(this); +#if CORE + TargetContext = XGraphicTargetContext.CORE; + _drawGraphics = false; + if (form.Owner != null) + _renderer = new XGraphicsPdfRenderer(form, this); + _pageSize = form.Size; + Initialize(); +#endif +#if GDI && !WPF + try + { + Lock.EnterGdiPlus(); + TargetContext = XGraphicTargetContext.GDI; + // If form.Owner is null create a meta file. + if (form.Owner == null) + { + MemoryStream stream = new MemoryStream(); + // BUG: This Windows 1.0 technique issued an exception under Microsoft Azure. // #??? + using (Graphics refgfx = Graphics.FromHwnd(IntPtr.Zero)) + { + IntPtr hdc = refgfx.GetHdc(); +#if true_ + // This code comes from my C++ RenderContext and checks some confusing details in connection + // with metafiles. + // Display | LaserJet + // DPI 96 : 120 | 300 + // physical device size in MM --------------------------------------------- + int horzSizeMM = NativeMethods.GetDeviceCaps(hdc, NativeMethods.HORZSIZE); // = 360 : 360 | 198 (not 210) + int vertSizeMM = NativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTSIZE); // = 290 : 290 | 288 (hot 297) + // Cool: + // My monitor is a Sony SDM-N80 and its size is EXACTLY 360mm x 290mm!! + // It is an 18.1" Flat Panel LCD from 2002 and these are the values + // an older display drivers reports in about 2003: + // Display + // DPI 96 : 120 + // -------------- + // 330 : 254 + // 254 : 203 + // Obviously my ATI driver reports the exact size of the monitor. + + + // device size in pixel + int horzSizePixel = NativeMethods.GetDeviceCaps(hdc, NativeMethods.HORZRES); // = 1280 : 1280 | 4676 + int vertSizePixel = NativeMethods.GetDeviceCaps(hdc, NativeMethods.VERTRES); // = 1024 : 1024 | 6814 + + // 'logical' device resolution in DPI + int logResX = NativeMethods.GetDeviceCaps(hdc, NativeMethods.LOGPIXELSX); // = 96 : 120 | 600 + int logResY = NativeMethods.GetDeviceCaps(hdc, NativeMethods.LOGPIXELSY); // = 96 : 120 | 600 + + // now we can get the 'physical' device resolution... + float phyResX = horzSizePixel / (horzSizeMM / 25.4f); // = 98.521210 : 128.00000 | 599.85052 + float phyResY = vertSizePixel / (vertSizeMM / 25.4f); // = 102.40000 : 128.12611 | 600.95691 + + // ...and rescale the size of the meta rectangle. + float magicX = logResX / phyResX; // = 0.97440946 : 0.93750000 | 1.0002491 + float magicY = logResY / phyResY; // = 0.93750000 : 0.93657720 | 0.99840766 + + // use A4 page in point + // adjust size of A4 page so that meta file fits with DrawImage... + //GdiRectF rcMagic = new GdiRectF(0, 0, magicX * form.Width, magicY * form.Height); + //m_PreviewMetafile = new Metafile(hdc, rcMagic, MetafileFrameUnitPoint, + // EmfTypeEmfPlusOnly, L"some description"); +#endif + GdiRectF rect = new GdiRectF(0, 0, form.PixelWidth, form.PixelHeight); + Metafile = new Metafile(stream, hdc, rect, MetafileFrameUnit.Pixel); //, EmfType.EmfPlusOnly); + + // Petzold disposes the refgfx object, although the hdc is in use of the metafile + refgfx.ReleaseHdc(hdc); + } // refgfx.Dispose(); + + _gfx = Graphics.FromImage(Metafile); + _gfx.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; + _drawGraphics = true; + } + else + { + Metafile = null; + _gfx = Graphics.FromHwnd(IntPtr.Zero); + } + if (form.Owner != null) + _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(form, this); + _pageSize = form.Size; + } + finally { Lock.ExitGdiPlus(); } + Initialize(); +#endif +#if WPF && !GDI + TargetContext = XGraphicTargetContext.WPF; +#if !SILVERLIGHT + // If form.Owner is null create a meta file. + if (form.Owner == null) + { + _dv = new DrawingVisual(); + _dc = _dv.RenderOpen(); + _drawGraphics = true; + } + else + { + _dv = new DrawingVisual(); + _dc = _dv.RenderOpen(); + } + if (form.Owner != null) + _renderer = new PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer(form, this); + _pageSize = form.Size; + Initialize(); +#else + throw new NotImplementedException(); // AGHACK + //Initialize(); +#endif +#endif + } + + /// + /// Creates the measure context. This is a graphics context created only for querying measures of text. + /// Drawing on a measure context has no effect. + /// + public static XGraphics CreateMeasureContext(XSize size, XGraphicsUnit pageUnit, XPageDirection pageDirection) + { +#if CORE + //throw new InvalidOperationException("No measure context in CORE build."); + PdfDocument dummy = new PdfDocument(); + PdfPage page = dummy.AddPage(); + //XGraphics gfx = new XGraphics(((System.Drawing.Graphics)null, size, pageUnit, pageDirection); + XGraphics gfx = XGraphics.FromPdfPage(page, XGraphicsPdfPageOptions.Append, pageUnit, pageDirection); + return gfx; +#endif +#if GDI && !WPF + //XGraphics gfx = new XGraphics((System.Drawing.Graphics)null, size, pageUnit, pageDirection); + XGraphics gfx = new XGraphics((System.Drawing.Graphics)null, size, pageUnit, pageDirection); + return gfx; +#endif +#if WPF && !SILVERLIGHT + XGraphics gfx = new XGraphics((System.Windows.Media.DrawingContext)null, size, pageUnit, pageDirection); + return gfx; +#endif +#if SILVERLIGHT + XGraphics gfx = new XGraphics(new Canvas(), size, pageUnit, pageDirection); + return gfx; +#endif +#if NETFX_CORE || UWP || DNC10 // NETFX_CORE_TODO + return null; +#endif + } + +#if GDI + /// + /// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object. + /// + public static XGraphics FromGraphics(Graphics graphics, XSize size) + { + // Creating a new instance is by design. + return new XGraphics(graphics, size, XGraphicsUnit.Point, XPageDirection.Downwards); + } + + /// + /// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object. + /// + public static XGraphics FromGraphics(Graphics graphics, XSize size, XGraphicsUnit unit) + { + // Creating a new instance is by design. + return new XGraphics(graphics, size, unit, XPageDirection.Downwards); + } + + ///// + ///// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object. + ///// + //public static XGraphics FromGraphics(Graphics graphics, XSize size, XPageDirection pageDirection) + //{ + // // Creating a new instance is by design. + // return new XGraphics(graphics, size, XGraphicsUnit.Point, pageDirection); + //} + + ///// + ///// Creates a new instance of the XGraphics class from a System.Drawing.Graphics object. + ///// + //public static XGraphics FromGraphics(Graphics graphics, XSize size, XGraphicsUnit unit, XPageDirection pageDirection) + //{ + // // Creating a new instance is by design. + // return new XGraphics(graphics, size, XGraphicsUnit.Point, pageDirection); + //} +#endif + +#if WPF && !SILVERLIGHT + /// + /// Creates a new instance of the XGraphics class from a System.Windows.Media.DrawingContext object. + /// + public static XGraphics FromDrawingContext(DrawingContext drawingContext, XSize size, XGraphicsUnit unit) + { + return new XGraphics(drawingContext, size, unit, XPageDirection.Downwards); + } +#endif + +#if WPF + /// + /// Creates a new instance of the XGraphics class from a System.Windows.Media.DrawingContext object. + /// + public static XGraphics FromCanvas(Canvas canvas, XSize size, XGraphicsUnit unit) + { + return new XGraphics(canvas, size, unit, XPageDirection.Downwards); + } +#endif + +#if UWP + /// + /// Creates a new instance of the XGraphics class from a Microsoft.Graphics.Canvas.CanvasDrawingSession object. + /// + public static XGraphics FromCanvasDrawingSession(CanvasDrawingSession drawingSession, XSize size, XGraphicsUnit unit) + { + return new XGraphics(drawingSession, size, unit, XPageDirection.Downwards); + } +#endif + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page) + { + XGraphics gfx = new XGraphics(page, XGraphicsPdfPageOptions.Append, XGraphicsUnit.Point, XPageDirection.Downwards); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XGraphicsUnit unit) + { + XGraphics gfx = new XGraphics(page, XGraphicsPdfPageOptions.Append, unit, XPageDirection.Downwards); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XPageDirection pageDirection) + { + XGraphics gfx = new XGraphics(page, XGraphicsPdfPageOptions.Append, XGraphicsUnit.Point, pageDirection); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options) + { + XGraphics gfx = new XGraphics(page, options, XGraphicsUnit.Point, XPageDirection.Downwards); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XPageDirection pageDirection) + { + XGraphics gfx = new XGraphics(page, options, XGraphicsUnit.Point, pageDirection); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit unit) + { + XGraphics gfx = new XGraphics(page, options, unit, XPageDirection.Downwards); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Pdf.PdfPage object. + /// + public static XGraphics FromPdfPage(PdfPage page, XGraphicsPdfPageOptions options, XGraphicsUnit unit, XPageDirection pageDirection) + { + XGraphics gfx = new XGraphics(page, options, unit, pageDirection); + if (page.Owner._uaManager != null) + page.Owner.Events.OnPageGraphicsCreated(page.Owner, new PageGraphicsEventArgs { Page = page, Graphics = gfx, ActionType = PageGraphicsActionType.GraphicsCreated }); // @PDF/UA + return gfx; + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XPdfForm object. + /// + public static XGraphics FromPdfForm(XPdfForm form) + { + if (form.Gfx != null) + return form.Gfx; + + return new XGraphics(form); + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XForm object. + /// + public static XGraphics FromForm(XForm form) + { + if (form.Gfx != null) + return form.Gfx; + + return new XGraphics(form); + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XForm object. + /// + public static XGraphics FromImage(XImage image) + { + return FromImage(image, XGraphicsUnit.Point); + } + + /// + /// Creates a new instance of the XGraphics class from a PdfSharp.Drawing.XImage object. + /// + public static XGraphics FromImage(XImage image, XGraphicsUnit unit) + { + if (image == null) + throw new ArgumentNullException("image"); + + XBitmapImage bmImage = image as XBitmapImage; + if (bmImage != null) + { +#if CORE + return null; +#endif +#if GDI && !WPF + Graphics gfx = Graphics.FromImage(image._gdiImage); + image.XImageState = image.XImageState | XImageState.UsedInDrawingContext; + return new XGraphics(gfx, new XSize(image.PixelWidth, image.PixelHeight), unit, XPageDirection.Downwards); +#endif +#if WPF && !GDI + DiagnosticsHelper.ThrowNotImplementedException("WPF image"); + return null; +#endif +#if NETFX_CORE + DiagnosticsHelper.ThrowNotImplementedException("NETFX_CORE image"); + return null; +#endif + } + return null; + } + + /// + /// Internal setup. + /// + void Initialize() + { + _pageOrigin = new XPoint(); + + double pageHeight = _pageSize.Height; + PdfPage targetPage = PdfPage; + XPoint trimOffset = new XPoint(); + if (targetPage != null && targetPage.TrimMargins.AreSet) + { + pageHeight += targetPage.TrimMargins.Top.Point + targetPage.TrimMargins.Bottom.Point; + trimOffset = new XPoint(targetPage.TrimMargins.Left.Point, targetPage.TrimMargins.Top.Point); + } + + XMatrix matrix = new XMatrix(); +#if CORE + // Nothing to do here. + Debug.Assert(TargetContext == XGraphicTargetContext.CORE); +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterFontFactory(); + if (_gfx != null) + matrix = _gfx.Transform; + + if (_pageUnit != XGraphicsUnit.Point) + { + switch (_pageUnit) + { + case XGraphicsUnit.Inch: + matrix.ScalePrepend(XUnit.InchFactor); + break; + + case XGraphicsUnit.Millimeter: + matrix.ScalePrepend(XUnit.MillimeterFactor); + break; + + case XGraphicsUnit.Centimeter: + matrix.ScalePrepend(XUnit.CentimeterFactor); + break; + + case XGraphicsUnit.Presentation: + matrix.ScalePrepend(XUnit.PresentationFactor); + break; + } + if (_gfx != null) + _gfx.Transform = (GdiMatrix)matrix; + } + } + finally { Lock.ExitFontFactory(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + if (_pageUnit != XGraphicsUnit.Presentation) + { + switch (_pageUnit) + { + case XGraphicsUnit.Point: + matrix.ScalePrepend(XUnit.PointFactorWpf); + break; + + case XGraphicsUnit.Inch: + matrix.ScalePrepend(XUnit.InchFactorWpf); + break; + + case XGraphicsUnit.Millimeter: + matrix.ScalePrepend(XUnit.MillimeterFactorWpf); + break; + + case XGraphicsUnit.Centimeter: + matrix.ScalePrepend(XUnit.CentimeterFactorWpf); + break; + } + if (!matrix.IsIdentity) + { +#if !SILVERLIGHT + MatrixTransform transform = new MatrixTransform((SysMatrix)matrix); + _dc.PushTransform(transform); +#else + MatrixTransform transform2 = new MatrixTransform(); + transform2.Matrix = (SysMatrix)matrix; + _dc.PushTransform(transform2); +#endif + } + } + } +#endif + if (_pageDirection != XPageDirection.Downwards) + matrix.Prepend(new XMatrix(1, 0, 0, -1, 0, pageHeight)); + + if (trimOffset != new XPoint()) + matrix.TranslatePrepend(trimOffset.X, -trimOffset.Y); + + DefaultViewMatrix = matrix; + _transform = new XMatrix(); + } + + /// + /// Releases all resources used by this object. + /// + public void Dispose() + { + Dispose(true); + } + + void Dispose(bool disposing) + { + if (!_disposed) + { + _disposed = true; + if (disposing) + { + // Dispose managed resources. + if (_associatedImage != null) + { + _associatedImage.DisassociateWithGraphics(this); + _associatedImage = null; + } + } + + if (_form != null) + _form.Finish(); +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + // GDI+ requires this to disassociate it from metafiles. + if (_gfx != null) + _gfx.Dispose(); + _gfx = null; + Metafile = null; + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (_dc != null) + { + _dc.Close(); +#if !SILVERLIGHT + // Free resources. Only needed when running on a server, but does no harm with desktop applications. + // Needed on server, but causes harm with WPF desktop application. So now what? + //_dc.Dispatcher.InvokeShutdown(); + + _dv = null; +#endif + } +#endif + _drawGraphics = false; + + if (_renderer != null) + { + _renderer.Close(); + _renderer = null; + } + } + } + bool _disposed; + + /// + /// Internal hack for MigraDoc. Will be removed in further releases. + /// Unicode support requires a global refactoring of MigraDoc and will be done in further releases. + /// + // ReSharper disable once InconsistentNaming + // ReSharper disable once ConvertToAutoProperty + public PdfFontEncoding MUH // MigraDoc Unicode Hack... + { + get { return _muh; } + set { _muh = value; } + } + PdfFontEncoding _muh; + + /// + /// A value indicating whether GDI+ or WPF is used as context. + /// + internal XGraphicTargetContext TargetContext; + + /// + /// Gets or sets the unit of measure used for page coordinates. + /// CURRENTLY ONLY POINT IS IMPLEMENTED. + /// + public XGraphicsUnit PageUnit + { + get { return _pageUnit; } + //set + //{ + // // TODO: other page units + // if (value != XGraphicsUnit.Point) + // throw new NotImplementedException("PageUnit must be XGraphicsUnit.Point in current implementation."); + //} + } + readonly XGraphicsUnit _pageUnit; + + /// + /// Gets or sets the a value indicating in which direction y-value grow. + /// + public XPageDirection PageDirection + { + get { return _pageDirection; } + set + { + // Is there really anybody who needs the concept of XPageDirection.Upwards? + if (value != XPageDirection.Downwards) + throw new NotImplementedException("PageDirection must be XPageDirection.Downwards in current implementation."); + } + } + readonly XPageDirection _pageDirection; + + /// + /// Gets the current page origin. Setting the origin is not yet implemented. + /// + public XPoint PageOrigin + { + get { return _pageOrigin; } + set + { + // Is there really anybody who needs to set the page origin? + if (value != new XPoint()) + throw new NotImplementedException("PageOrigin cannot be modified in current implementation."); + } + } + XPoint _pageOrigin; + + /// + /// Gets the current size of the page. + /// + public XSize PageSize + { + get { return _pageSize; } + //set + //{ + // //TODO + // throw new NotImplementedException("PageSize cannot be modified in current implementation."); + //} + } + XSize _pageSize; + XSize _pageSizePoints; + + #region Drawing + + // ----- DrawLine ----------------------------------------------------------------------------- + +#if GDI + /// + /// Draws a line connecting two Point structures. + /// + public void DrawLine(XPen pen, GdiPoint pt1, GdiPoint pt2) + { + // Because of overloading the cast is NOT redundant. + DrawLine(pen, (double)pt1.X, (double)pt1.Y, (double)pt2.X, (double)pt2.Y); + } +#endif + +#if WPF + /// + /// Draws a line connecting two Point structures. + /// + public void DrawLine(XPen pen, SysPoint pt1, SysPoint pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } +#endif + +#if GDI + /// + /// Draws a line connecting two GdiPointF structures. + /// + public void DrawLine(XPen pen, GdiPointF pt1, GdiPointF pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } +#endif + + /// + /// Draws a line connecting two XPoint structures. + /// + public void DrawLine(XPen pen, XPoint pt1, XPoint pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Draws a line connecting the two points specified by coordinate pairs. + /// + public void DrawLine(XPen pen, double x1, double y1, double x2, double y2) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawLine(pen.RealizeGdiPen(), (float)x1, (float)y1, (float)x2, (float)y2); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawLine(pen.RealizeWpfPen(), new SysPoint(x1, y1), new SysPoint(x2, y2)); +#endif +#if UWP + _cds.DrawLine(new Vector2((float)x1, (float)x2), new Vector2((float)x2, (float)y2), Colors.Red, (float)pen.Width); +#endif + } + + if (_renderer != null) + _renderer.DrawLines(pen, new[] { new XPoint(x1, y1), new XPoint(x2, y2) }); + } + + // ----- DrawLines ---------------------------------------------------------------------------- + +#if GDI + /// + /// Draws a series of line segments that connect an array of points. + /// + public void DrawLines(XPen pen, GdiPoint[] points) + { + DrawLines(pen, MakePointFArray(points, 0, points.Length)); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Draws a series of line segments that connect an array of points. + /// + public void DrawLines(XPen pen, SysPoint[] points) + { + DrawLines(pen, XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Draws a series of line segments that connect an array of points. + /// + public void DrawLines(XPen pen, GdiPointF[] points) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + if (_drawGraphics) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawLines(pen.RealizeGdiPen(), points); + } + finally { Lock.ExitGdiPlus(); } + } + + if (_renderer != null) + _renderer.DrawLines(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Draws a series of line segments that connect an array of points. + /// + public void DrawLines(XPen pen, XPoint[] points) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawLines(pen.RealizeGdiPen(), XGraphics.MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { +#if !SILVERLIGHT + PolyLineSegment seg = new PolyLineSegment(XGraphics.MakePointArray(points), true); +#else + Point[] pts = XGraphics.MakePointArray(points); + PointCollection collection = new PointCollection(); + foreach (Point point in pts) + collection.Add(point); + PolyLineSegment seg = new PolyLineSegment(); + seg.Points = collection; +#endif + PathFigure figure = new PathFigure(); + figure.IsFilled = false; + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + figure.Segments.Add(seg); + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo); + } +#endif +#if UWP + var pathBuilder = new CanvasPathBuilder(_cds.Device); + pathBuilder.BeginFigure((float)points[0].X, (float)points[0].Y, CanvasFigureFill.DoesNotAffectFills); + int length = points.Length; + for (int idx = 1; idx < length; idx++) + pathBuilder.AddLine((float)points[idx].X, (float)points[idx].Y); + pathBuilder.EndFigure(CanvasFigureLoop.Open); + var geometry = CanvasGeometry.CreatePath(pathBuilder); + _cds.DrawGeometry(geometry, Colors.Red); +#endif + } + + if (_renderer != null) + _renderer.DrawLines(pen, points); + } + + /// + /// Draws a series of line segments that connect an array of x and y pairs. + /// + public void DrawLines(XPen pen, double x, double y, params double[] value) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (value == null) + throw new ArgumentNullException("value"); + + int length = value.Length; + XPoint[] points = new XPoint[length / 2 + 1]; + points[0].X = x; + points[0].Y = y; + for (int idx = 0; idx < length / 2; idx++) + { + points[idx + 1].X = value[2 * idx]; + points[idx + 1].Y = value[2 * idx + 1]; + } + DrawLines(pen, points); + } + + // ----- DrawBezier --------------------------------------------------------------------------- + +#if GDI + /// + /// Draws a Bzier spline defined by four points. + /// + public void DrawBezier(XPen pen, GdiPoint pt1, GdiPoint pt2, GdiPoint pt3, GdiPoint pt4) + { + // ReSharper disable RedundantCast because it is required + DrawBezier(pen, (double)pt1.X, (double)pt1.Y, (double)pt2.X, (double)pt2.Y, + (double)pt3.X, (double)pt3.Y, (double)pt4.X, (double)pt4.Y); + // ReSharper restore RedundantCast + } +#endif + +#if WPF + /// + /// Draws a Bzier spline defined by four points. + /// + public void DrawBezier(XPen pen, SysPoint pt1, SysPoint pt2, SysPoint pt3, SysPoint pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } +#endif + +#if GDI + /// + /// Draws a Bzier spline defined by four points. + /// + public void DrawBezier(XPen pen, GdiPointF pt1, GdiPointF pt2, GdiPointF pt3, GdiPointF pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } +#endif + + /// + /// Draws a Bzier spline defined by four points. + /// + public void DrawBezier(XPen pen, XPoint pt1, XPoint pt2, XPoint pt3, XPoint pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Draws a Bzier spline defined by four points. + /// + public void DrawBezier(XPen pen, double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawBezier(pen.RealizeGdiPen(), (float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3, (float)x4, (float)y4); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { +#if !SILVERLIGHT + BezierSegment seg = new BezierSegment(new SysPoint(x2, y2), new SysPoint(x3, y3), new SysPoint(x4, y4), true); +#else + BezierSegment seg = new BezierSegment(); + seg.Point1 = new SysPoint(x2, y2); + seg.Point2 = new SysPoint(x3, y3); + seg.Point3 = new SysPoint(x4, y4); +#endif + PathFigure figure = new PathFigure(); + figure.StartPoint = new SysPoint(x1, y1); + figure.Segments.Add(seg); + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawBeziers(pen, + new XPoint[] { new XPoint(x1, y1), new XPoint(x2, y2), new XPoint(x3, y3), new XPoint(x4, y4) }); + } + + // ----- DrawBeziers -------------------------------------------------------------------------- + +#if GDI + /// + /// Draws a series of Bzier splines from an array of points. + /// + public void DrawBeziers(XPen pen, GdiPoint[] points) + { + DrawBeziers(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if WPF + /// + /// Draws a series of Bzier splines from an array of points. + /// + public void DrawBeziers(XPen pen, SysPoint[] points) + { + DrawBeziers(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Draws a series of Bzier splines from an array of points. + /// + public void DrawBeziers(XPen pen, GdiPointF[] points) + { + DrawBeziers(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Draws a series of Bzier splines from an array of points. + /// + public void DrawBeziers(XPen pen, XPoint[] points) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + int count = points.Length; + if (count == 0) + return; + + if ((count - 1) % 3 != 0) + throw new ArgumentException("Invalid number of points for bezier curves. Number must fulfill 4+3n.", "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawBeziers(pen.RealizeGdiPen(), MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + PathFigure figure = new PathFigure(); + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx += 3) + { +#if !SILVERLIGHT + BezierSegment seg = new BezierSegment( + new SysPoint(points[idx].X, points[idx].Y), + new SysPoint(points[idx + 1].X, points[idx + 1].Y), + new SysPoint(points[idx + 2].X, points[idx + 2].Y), true); +#else + BezierSegment seg = new BezierSegment(); + seg.Point1 = new SysPoint(points[idx].X, points[idx].Y); + seg.Point2 = new SysPoint(points[idx + 1].X, points[idx + 1].Y); + seg.Point3 = new SysPoint(points[idx + 2].X, points[idx + 2].Y); +#endif + figure.Segments.Add(seg); + } + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawBeziers(pen, points); + } + + // ----- DrawCurve ---------------------------------------------------------------------------- + +#if GDI + /// + /// Draws a cardinal spline through a specified array of points. + /// + public void DrawCurve(XPen pen, GdiPoint[] points) + { + DrawCurve(pen, MakePointFArray(points, 0, points.Length), 0.5); + } + + /// + /// Draws a cardinal spline through a specified array of point using a specified tension. + /// The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, GdiPoint[] points, int offset, int numberOfSegments, double tension) + { + DrawCurve(pen, MakePointFArray(points, offset, numberOfSegments), tension); + } +#endif + +#if WPF + /// + /// Draws a cardinal spline through a specified array of points. + /// + public void DrawCurve(XPen pen, SysPoint[] points) + { + DrawCurve(pen, MakeXPointArray(points, 0, points.Length), 0.5); + } + + /// + /// Draws a cardinal spline through a specified array of point. The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, SysPoint[] points, int offset, int numberOfSegments) + { + DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), 0.5); + } +#endif + +#if GDI + /// + /// Draws a cardinal spline through a specified array of points. + /// + public void DrawCurve(XPen pen, GdiPointF[] points) + { + DrawCurve(pen, MakeXPointArray(points, 0, points.Length), 0.5); + } +#endif + + /// + /// Draws a cardinal spline through a specified array of points. + /// + public void DrawCurve(XPen pen, XPoint[] points) + { + DrawCurve(pen, points, 0.5); + } + +#if GDI + /// + /// Draws a cardinal spline through a specified array of points using a specified tension. + /// + public void DrawCurve(XPen pen, GdiPoint[] points, double tension) + { + DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension); + } +#endif + +#if WPF + /// + /// Draws a cardinal spline through a specified array of points using a specified tension. + /// + public void DrawCurve(XPen pen, SysPoint[] points, double tension) + { + DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension); + } + + /// + /// Draws a cardinal spline through a specified array of point using a specified tension. + /// The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, SysPoint[] points, int offset, int numberOfSegments, double tension) + { + DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), tension); + } +#endif + +#if GDI + /// + /// Draws a cardinal spline through a specified array of points using a specified tension. + /// + public void DrawCurve(XPen pen, GdiPointF[] points, double tension) + { + DrawCurve(pen, MakeXPointArray(points, 0, points.Length), tension); + } + + /// + /// Draws a cardinal spline through a specified array of point. The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, GdiPointF[] points, int offset, int numberOfSegments) + { + DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), 0.5); + } + + /// + /// Draws a cardinal spline through a specified array of point using a specified tension. + /// The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, GdiPointF[] points, int offset, int numberOfSegments, double tension) + { + DrawCurve(pen, MakeXPointArray(points, offset, numberOfSegments), tension); + } +#endif + + /// + /// Draws a cardinal spline through a specified array of point using a specified tension. + /// The drawing begins offset from the beginning of the array. + /// + public void DrawCurve(XPen pen, XPoint[] points, int offset, int numberOfSegments, double tension) + { + XPoint[] points2 = new XPoint[numberOfSegments]; + Array.Copy(points, offset, points2, 0, numberOfSegments); + DrawCurve(pen, points2, tension); + } + + /// + /// Draws a cardinal spline through a specified array of points using a specified tension. + /// + public void DrawCurve(XPen pen, XPoint[] points, double tension) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count < 2) + throw new ArgumentException("DrawCurve requires two or more points.", "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawCurve(pen.RealizeGdiPen(), MakePointFArray(points), (float)tension); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + tension /= 3; + + PathFigure figure = new PathFigure(); + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + if (count == 2) + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); + } + else + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension)); + for (int idx = 1; idx < count - 2; idx++) + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension)); + } + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawCurve(pen, points, tension); + } + + // ----- DrawArc ------------------------------------------------------------------------------ + +#if GDI + /// + /// Draws an arc representing a portion of an ellipse. + /// + public void DrawArc(XPen pen, Rectangle rect, double startAngle, double sweepAngle) + { + // Because of overloading the cast is NOT redundant. + DrawArc(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle); + } +#endif + +#if GDI + /// + /// Draws an arc representing a portion of an ellipse. + /// + public void DrawArc(XPen pen, GdiRectF rect, double startAngle, double sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Draws an arc representing a portion of an ellipse. + /// + public void DrawArc(XPen pen, XRect rect, double startAngle, double sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws an arc representing a portion of an ellipse. + /// + public void DrawArc(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + if (Math.Abs(sweepAngle) >= 360) + { + DrawEllipse(pen, x, y, width, height); + } + else + { + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + SysPoint startPoint; + ArcSegment seg = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startPoint); + PathFigure figure = new PathFigure(); + figure.StartPoint = startPoint; + figure.Segments.Add(seg); + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(null, pen.RealizeWpfPen(), geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawArc(pen, x, y, width, height, startAngle, sweepAngle); + } + } + + // ----- DrawRectangle ------------------------------------------------------------------------ + + // ----- stroke ----- + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, Rectangle rect) + { + // Because of overloading the cast is NOT redundant. + DrawRectangle(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, GdiRectF rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, XRect rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, double x, double y, double width, double height) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawRectangle(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + _dc.DrawRectangle(null, pen.RealizeWpfPen(), new Rect(x, y, width, height)); + } +#endif +#if UWP + if (TargetContext == XGraphicTargetContext.UWP) + { + _cds.DrawRectangle((float)x, (float)y, (float)width, (float)height, pen.Color.ToUwpColor()); + } +#endif + } + + if (_renderer != null) + _renderer.DrawRectangle(pen, null, x, y, width, height); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XBrush brush, Rectangle rect) + { + // Because of overloading the cast is NOT redundant. + DrawRectangle(brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XBrush brush, GdiRectF rect) + { + DrawRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XBrush brush, XRect rect) + { + DrawRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XBrush brush, double x, double y, double width, double height) + { + if (brush == null) + throw new ArgumentNullException("brush"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.FillRectangle(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawRectangle(brush.RealizeWpfBrush(), null, new Rect(x, y, width, height)); +#endif +#if UWP + if (TargetContext == XGraphicTargetContext.UWP) + { + _cds.DrawRectangle((float)x, (float)y, (float)width, (float)height, brush.RealizeCanvasBrush()); + } +#endif + } + + if (_renderer != null) + _renderer.DrawRectangle(null, brush, x, y, width, height); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, XBrush brush, Rectangle rect) + { + // Because of overloading the cast is NOT redundant. + DrawRectangle(pen, brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, XBrush brush, GdiRectF rect) + { + DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, XBrush brush, XRect rect) + { + DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws a rectangle. + /// + public void DrawRectangle(XPen pen, XBrush brush, double x, double y, double width, double height) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillRectangle(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height); + if (pen != null) + _gfx.DrawRectangle(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawRectangle( + brush != null ? brush.RealizeWpfBrush() : null, + pen != null ? pen.RealizeWpfPen() : null, + new Rect(x, y, width, height)); +#endif + } + + if (_renderer != null) + _renderer.DrawRectangle(pen, brush, x, y, width, height); + } + + // ----- DrawRectangles ----------------------------------------------------------------------- + + // ----- stroke ----- + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, GdiRect[] rectangles) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(pen, null, rectangles); + } +#endif + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, GdiRectF[] rectangles) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(pen, null, rectangles); + } +#endif + + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, XRect[] rectangles) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(pen, null, rectangles); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XBrush brush, GdiRect[] rectangles) + { + if (brush == null) + throw new ArgumentNullException("brush"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(null, brush, rectangles); + } +#endif + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XBrush brush, GdiRectF[] rectangles) + { + if (brush == null) + throw new ArgumentNullException("brush"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(null, brush, rectangles); + } +#endif + + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XBrush brush, XRect[] rectangles) + { + if (brush == null) + throw new ArgumentNullException("brush"); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + DrawRectangles(null, brush, rectangles); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, XBrush brush, Rectangle[] rectangles) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + if (_drawGraphics) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillRectangles(brush.RealizeGdiBrush(), rectangles); + if (pen != null) + _gfx.DrawRectangles(pen.RealizeGdiPen(), rectangles); + } + finally { Lock.ExitGdiPlus(); } + } + if (_renderer != null) + { + int count = rectangles.Length; + for (int idx = 0; idx < count; idx++) + { + Rectangle rect = rectangles[idx]; + _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + } + } +#endif + +#if GDI + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, XBrush brush, GdiRectF[] rectangles) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + if (_drawGraphics) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillRectangles(brush.RealizeGdiBrush(), rectangles); + if (pen != null) + _gfx.DrawRectangles(pen.RealizeGdiPen(), rectangles); + } + finally { Lock.ExitGdiPlus(); } + } + if (_renderer != null) + { + int count = rectangles.Length; + for (int idx = 0; idx < count; idx++) + { + GdiRectF rect = rectangles[idx]; + _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + } + } +#endif + + /// + /// Draws a series of rectangles. + /// + public void DrawRectangles(XPen pen, XBrush brush, XRect[] rectangles) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + if (rectangles == null) + throw new ArgumentNullException("rectangles"); + + int count = rectangles.Length; + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + GdiRectF[] rects = MakeRectangleFArray(rectangles, 0, rectangles.Length); + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillRectangles(brush.RealizeGdiBrush(), rects); + if (pen != null) + _gfx.DrawRectangles(pen.RealizeGdiPen(), rects); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null; + WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null; + for (int idx = 0; idx < count; idx++) + { + XRect rect = rectangles[idx]; + _dc.DrawRectangle(wpfBrush, wpfPen, new SysRect(new SysPoint(rect.X, rect.Y), new SysSize(rect.Width, rect.Height))); + } + } +#endif + } + + if (_renderer != null) + { + for (int idx = 0; idx < count; idx++) + { + XRect rect = rectangles[idx]; + _renderer.DrawRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + } + } + + // ----- DrawRoundedRectangle ----------------------------------------------------------------- + + // ----- stroke ----- + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, Rectangle rect, GdiSize ellipseSize) + { + DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if WPF + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, Rect rect, SysSize ellipseSize) + { + DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, GdiRectF rect, SizeF ellipseSize) + { + DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XRect rect, XSize ellipseSize) + { + DrawRoundedRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + DrawRoundedRectangle(pen, null, x, y, width, height, ellipseWidth, ellipseHeight); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XBrush brush, Rectangle rect, GdiSize ellipseSize) + { + DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if WPF + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XBrush brush, Rect rect, SysSize ellipseSize) + { + DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XBrush brush, GdiRectF rect, SizeF ellipseSize) + { + DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XBrush brush, XRect rect, XSize ellipseSize) + { + DrawRoundedRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XBrush brush, double x, double y, double width, double height, double ellipseWidth, double ellipseHeight) + { + if (brush == null) + throw new ArgumentNullException("brush"); + + DrawRoundedRectangle(null, brush, x, y, width, height, ellipseWidth, ellipseHeight); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XBrush brush, Rectangle rect, GdiSize ellipseSize) + { + // ReSharper disable RedundantCast because it is required + DrawRoundedRectangle(pen, brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, + (double)ellipseSize.Width, (double)ellipseSize.Height); + // ReSharper restore RedundantCast + } +#endif + +#if WPF + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XBrush brush, Rect rect, SysSize ellipseSize) + { + DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if GDI + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XBrush brush, GdiRectF rect, SizeF ellipseSize) + { + DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XBrush brush, XRect rect, XSize ellipseSize) + { + DrawRoundedRectangle(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } + + /// + /// Draws a rectangles with round corners. + /// + public void DrawRoundedRectangle(XPen pen, XBrush brush, double x, double y, double width, double height, + double ellipseWidth, double ellipseHeight) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + XGraphicsPath path = new XGraphicsPath(); + path.AddRoundedRectangle(x, y, width, height, ellipseWidth, ellipseHeight); + DrawPath(pen, brush, path); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + _dc.DrawRoundedRectangle( + brush != null ? brush.RealizeWpfBrush() : null, + pen != null ? pen.RealizeWpfPen() : null, + new Rect(x, y, width, height), ellipseWidth / 2, ellipseHeight / 2); + } +#endif + } + + if (_renderer != null) + _renderer.DrawRoundedRectangle(pen, brush, x, y, width, height, ellipseWidth, ellipseHeight); + } + + // ----- DrawEllipse -------------------------------------------------------------------------- + + // ----- stroke ----- + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, Rectangle rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, GdiRectF rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, XRect rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, double x, double y, double width, double height) + { + if (pen == null) + throw new ArgumentNullException("pen"); + + // No DrawArc defined? + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, 0, 360); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + double radiusX = width / 2; + double radiusY = height / 2; + _dc.DrawEllipse(null, pen.RealizeWpfPen(), new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY); + } +#endif + } + + if (_renderer != null) + _renderer.DrawEllipse(pen, null, x, y, width, height); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XBrush brush, Rectangle rect) + { + DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XBrush brush, GdiRectF rect) + { + DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XBrush brush, XRect rect) + { + DrawEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XBrush brush, double x, double y, double width, double height) + { + if (brush == null) + throw new ArgumentNullException("brush"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.FillEllipse(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + double radiusX = width / 2; + double radiusY = height / 2; + _dc.DrawEllipse(brush.RealizeWpfBrush(), null, new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY); + } +#endif + } + + if (_renderer != null) + _renderer.DrawEllipse(null, brush, x, y, width, height); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, XBrush brush, Rectangle rect) + { + DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + +#if GDI + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, XBrush brush, GdiRectF rect) + { + DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, XBrush brush, XRect rect) + { + DrawEllipse(pen, brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(XPen pen, XBrush brush, double x, double y, double width, double height) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillEllipse(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height); + if (pen != null) + _gfx.DrawArc(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, 0, 360); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + double radiusX = width / 2; + double radiusY = height / 2; + _dc.DrawEllipse( + brush != null ? brush.RealizeWpfBrush() : null, + pen != null ? pen.RealizeWpfPen() : null, + new SysPoint(x + radiusX, y + radiusY), radiusX, radiusY); + } +#endif +#if UWP + //var cds = new CanvasDrawingSession(); + //cds.DrawCachedGeometry(); + + if (TargetContext == XGraphicTargetContext.UWP) + { + var radiusX = (float)width / 2; + var radiusY = (float)height / 2; + + //var geometry = CanvasGeometry.CreateEllipse(_cds.Device, (float)x + radiusX, (float)y + radiusY, radiusX, radiusY); + + if (brush != null) + _cds.FillEllipse((float)x + radiusX, (float)y + radiusY, radiusX, radiusY, Colors.Blue); + if (pen != null) + _cds.DrawEllipse((float)x + radiusX, (float)y + radiusY, radiusX, radiusY, pen.Color.ToUwpColor()); + } +#endif + } + + if (_renderer != null) + _renderer.DrawEllipse(pen, brush, x, y, width, height); + } + + // ----- DrawPolygon -------------------------------------------------------------------------- + + // ----- stroke ----- + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, GdiPoint[] points) + { + DrawPolygon(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if WPF + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, SysPoint[] points) + { + DrawPolygon(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, GdiPointF[] points) + { + DrawPolygon(pen, MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, XPoint[] points) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (points == null) + throw new ArgumentNullException("points"); + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawPolygon(pen.RealizeGdiPen(), MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + _dc.DrawGeometry(null, pen.RealizeWpfPen(), GeometryHelper.CreatePolygonGeometry(MakePointArray(points), XFillMode.Alternate, true)); + } +#endif + } + + if (_renderer != null) + _renderer.DrawPolygon(pen, null, points, XFillMode.Alternate); // XFillMode is ignored + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XBrush brush, GdiPoint[] points, XFillMode fillmode) + { + DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + +#if WPF + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XBrush brush, SysPoint[] points, XFillMode fillmode) + { + DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XBrush brush, GdiPointF[] points, XFillMode fillmode) + { + DrawPolygon(brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XBrush brush, XPoint[] points, XFillMode fillmode) + { + if (brush == null) + throw new ArgumentNullException("brush"); + if (points == null) + throw new ArgumentNullException("points"); + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.FillPolygon(brush.RealizeGdiBrush(), MakePointFArray(points), (FillMode)fillmode); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawGeometry(brush.RealizeWpfBrush(), null, GeometryHelper.CreatePolygonGeometry(MakePointArray(points), fillmode, true)); +#endif + } + + if (_renderer != null) + _renderer.DrawPolygon(null, brush, points, fillmode); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode) + { + DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + +#if WPF + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode) + { + DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + +#if GDI + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode) + { + DrawPolygon(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode); + } +#endif + + /// + /// Draws a polygon defined by an array of points. + /// + public void DrawPolygon(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + if (points == null) + throw new ArgumentNullException("points"); + if (points.Length < 2) + throw new ArgumentException(PSSR.PointArrayAtLeast(2), "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + GdiPointF[] pts = MakePointFArray(points); + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillPolygon(brush.RealizeGdiBrush(), pts, (FillMode)fillmode); + if (pen != null) + _gfx.DrawPolygon(pen.RealizeGdiPen(), pts); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null; + WpfPen wpfPen = brush != null ? pen.RealizeWpfPen() : null; + _dc.DrawGeometry(wpfBrush, wpfPen, GeometryHelper.CreatePolygonGeometry(MakePointArray(points), fillmode, true)); + } +#endif + } + + if (_renderer != null) + _renderer.DrawPolygon(pen, brush, points, fillmode); + } + + // ----- DrawPie ------------------------------------------------------------------------------ + + // ----- stroke ----- + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, Rectangle rect, double startAngle, double sweepAngle) + { + // ReSharper disable RedundantCast because it is required + DrawPie(pen, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle); + // ReSharper restore RedundantCast + } +#endif + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, GdiRectF rect, double startAngle, double sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, XRect rect, double startAngle, double sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, double x, double y, double width, double height, double startAngle, double sweepAngle) + { + if (pen == null) + throw new ArgumentNullException("pen", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawPie(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + DrawPie(pen, null, x, y, width, height, startAngle, sweepAngle); +#endif + } + + if (_renderer != null) + _renderer.DrawPie(pen, null, x, y, width, height, startAngle, sweepAngle); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XBrush brush, Rectangle rect, double startAngle, double sweepAngle) + { + // Because of overloading the cast is NOT redundant. + DrawPie(brush, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height, startAngle, sweepAngle); + } +#endif + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XBrush brush, GdiRectF rect, double startAngle, double sweepAngle) + { + DrawPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XBrush brush, XRect rect, double startAngle, double sweepAngle) + { + DrawPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XBrush brush, double x, double y, double width, double height, double startAngle, double sweepAngle) + { + if (brush == null) + throw new ArgumentNullException("brush", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.FillPie(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + DrawPie(null, brush, x, y, width, height, startAngle, sweepAngle); +#endif + } + + if (_renderer != null) + _renderer.DrawPie(null, brush, x, y, width, height, startAngle, sweepAngle); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, XBrush brush, Rectangle rect, double startAngle, double sweepAngle) + { + DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + +#if GDI + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, XBrush brush, GdiRectF rect, double startAngle, double sweepAngle) + { + DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, XBrush brush, XRect rect, double startAngle, double sweepAngle) + { + DrawPie(pen, brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws a pie defined by an ellipse. + /// + public void DrawPie(XPen pen, XBrush brush, double x, double y, double width, double height, double startAngle, double sweepAngle) + { + if (pen == null && brush == null) + throw new ArgumentNullException("pen", PSSR.NeedPenOrBrush); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillPie(brush.RealizeGdiBrush(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + if (pen != null) + _gfx.DrawPie(pen.RealizeGdiPen(), (float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null; + WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null; + SysPoint center = new SysPoint(x + width / 2, y + height / 2); + SysPoint startArc; + ArcSegment arc = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startArc); + PathFigure figure = new PathFigure(); + figure.StartPoint = center; +#if !SILVERLIGHT + LineSegment seg = new LineSegment(startArc, true); +#else + LineSegment seg = new LineSegment { Point = startArc }; +#endif + figure.Segments.Add(seg); + figure.Segments.Add(arc); + figure.IsClosed = true; + PathGeometry geo = new PathGeometry(); + geo.Figures.Add(figure); + _dc.DrawGeometry(wpfBrush, wpfPen, geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawPie(pen, brush, x, y, width, height, startAngle, sweepAngle); + } + + // ----- DrawClosedCurve ---------------------------------------------------------------------- + + // ----- stroke ----- + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, GdiPoint[] points) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, SysPoint[] points) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, GdiPointF[] points) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XPoint[] points) + { + DrawClosedCurve(pen, null, points, XFillMode.Alternate, 0.5); + } + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, GdiPoint[] points, double tension) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, SysPoint[] points, double tension) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, GdiPointF[] points, double tension) + { + DrawClosedCurve(pen, null, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, tension); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XPoint[] points, double tension) + { + DrawClosedCurve(pen, null, points, XFillMode.Alternate, tension); + } + + // ----- fill ----- + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPoint[] points) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, SysPoint[] points) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPointF[] points) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, XPoint[] points) + { + DrawClosedCurve(null, brush, points, XFillMode.Alternate, 0.5); + } + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, SysPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPointF[] points, XFillMode fillmode) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, XPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(null, brush, points, fillmode, 0.5); + } + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPoint[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, SysPoint[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, GdiPointF[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(null, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XBrush brush, XPoint[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(null, brush, points, fillmode, tension); + } + + // ----- stroke and fill ----- + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), XFillMode.Alternate, 0.5); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points) + { + DrawClosedCurve(pen, brush, points, XFillMode.Alternate, 0.5); + } + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, 0.5); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode) + { + DrawClosedCurve(pen, brush, points, fillmode, 0.5); + } + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPoint[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + +#if WPF + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, SysPoint[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + +#if GDI + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, GdiPointF[] points, XFillMode fillmode, double tension) + { + DrawClosedCurve(pen, brush, MakeXPointArray(points, 0, points.Length), fillmode, tension); + } +#endif + + /// + /// Draws a closed cardinal spline defined by an array of points. + /// + public void DrawClosedCurve(XPen pen, XBrush brush, XPoint[] points, XFillMode fillmode, double tension) + { + if (pen == null && brush == null) + { + // ReSharper disable once NotResolvedInText + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + } + + int count = points.Length; + if (count == 0) + return; + if (count < 2) + throw new ArgumentException("Not enough points.", "points"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillClosedCurve(brush.RealizeGdiBrush(), MakePointFArray(points), (FillMode)fillmode, (float)tension); + if (pen != null) + { + // The fillmode is not used by DrawClosedCurve. + _gfx.DrawClosedCurve(pen.RealizeGdiPen(), MakePointFArray(points), (float)tension, (FillMode)fillmode); + } + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + tension /= 3; // Simply tried out. Not proofed why it is correct. + + PathFigure figure = new PathFigure(); + figure.IsClosed = true; + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + if (count == 2) + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); + } + else + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 1], points[0], points[1], points[2], tension)); + for (int idx = 1; idx < count - 2; idx++) + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[0], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 2], points[count - 1], points[0], points[1], tension)); + } + PathGeometry geo = new PathGeometry(); + geo.FillRule = fillmode == XFillMode.Alternate ? FillRule.EvenOdd : FillRule.Nonzero; + geo.Figures.Add(figure); + WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null; + WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null; + _dc.DrawGeometry(wpfBrush, wpfPen, geo); + } +#endif + } + + if (_renderer != null) + _renderer.DrawClosedCurve(pen, brush, points, tension, fillmode); + } + + // ----- DrawPath ----------------------------------------------------------------------------- + + // ----- stroke ----- + + /// + /// Draws a graphical path. + /// + public void DrawPath(XPen pen, XGraphicsPath path) + { + if (pen == null) + throw new ArgumentNullException("pen"); + if (path == null) + throw new ArgumentNullException("path"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.DrawPath(pen.RealizeGdiPen(), path._gdipPath); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawGeometry(null, pen.RealizeWpfPen(), path._pathGeometry); +#endif + } + + if (_renderer != null) + _renderer.DrawPath(pen, null, path); + } + + // ----- fill ----- + + /// + /// Draws a graphical path. + /// + public void DrawPath(XBrush brush, XGraphicsPath path) + { + if (brush == null) + throw new ArgumentNullException("brush"); + if (path == null) + throw new ArgumentNullException("path"); + + if (_drawGraphics) + { +#if GDI + // $TODO THHO Lock??? + if (TargetContext == XGraphicTargetContext.GDI) + _gfx.FillPath(brush.RealizeGdiBrush(), path._gdipPath); +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + _dc.DrawGeometry(brush.RealizeWpfBrush(), null, path._pathGeometry); +#endif + } + + if (_renderer != null) + _renderer.DrawPath(null, brush, path); + } + + // ----- stroke and fill ----- + + /// + /// Draws a graphical path. + /// + public void DrawPath(XPen pen, XBrush brush, XGraphicsPath path) + { + if (pen == null && brush == null) + { + // ReSharper disable once NotResolvedInText + throw new ArgumentNullException("pen and brush", PSSR.NeedPenOrBrush); + } + if (path == null) + throw new ArgumentNullException("path"); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (brush != null) + _gfx.FillPath(brush.RealizeGdiBrush(), path._gdipPath); + if (pen != null) + _gfx.DrawPath(pen.RealizeGdiPen(), path._gdipPath); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + WpfBrush wpfBrush = brush != null ? brush.RealizeWpfBrush() : null; + WpfPen wpfPen = pen != null ? pen.RealizeWpfPen() : null; + _dc.DrawGeometry(wpfBrush, wpfPen, path._pathGeometry); + } +#endif + } + + if (_renderer != null) + _renderer.DrawPath(pen, brush, path); + } + + // ----- DrawString --------------------------------------------------------------------------- + +#if GDI + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, GdiPointF point) + { + DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), XStringFormats.Default); + } +#endif + + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, XPoint point) + { + DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), XStringFormats.Default); + } + +#if GDI + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, GdiPointF point, XStringFormat format) + { + DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), format); + } +#endif + + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, XPoint point, XStringFormat format) + { + DrawString(s, font, brush, new XRect(point.X, point.Y, 0, 0), format); + } + + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, double x, double y) + { + DrawString(s, font, brush, new XRect(x, y, 0, 0), XStringFormats.Default); + } + + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, double x, double y, XStringFormat format) + { + DrawString(s, font, brush, new XRect(x, y, 0, 0), format); + } + +#if GDI + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, GdiRectF layoutRectangle) + { + DrawString(s, font, brush, new XRect(layoutRectangle), XStringFormats.Default); + } +#endif + + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, XRect layoutRectangle) + { + DrawString(s, font, brush, layoutRectangle, XStringFormats.Default); + } + +#if GDI + /// + /// Draws the specified text string. + /// + public void DrawString(string s, XFont font, XBrush brush, GdiRectF layoutRectangle, XStringFormat format) + { + DrawString(s, font, brush, new XRect(layoutRectangle), format); + } +#endif + + /// + /// Draws the specified text string. + /// + public void DrawString(string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format) + { + if (text == null) + throw new ArgumentNullException("text"); + if (font == null) + throw new ArgumentNullException("font"); + if (brush == null) + throw new ArgumentNullException("brush"); + + if (format != null && format.LineAlignment == XLineAlignment.BaseLine && layoutRectangle.Height != 0) + throw new InvalidOperationException("DrawString: With XLineAlignment.BaseLine the height of the layout rectangle must be 0."); + + if (text.Length == 0) + return; + + if (format == null) + format = XStringFormats.Default; + // format cannot be null below this line. + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + // Was font created with font resolver? + if (font.GdiFont == null) + throw new InvalidOperationException("This font cannot be used by GDI+."); + + try + { + Lock.EnterGdiPlus(); + GdiRectF rect = layoutRectangle.ToRectangleF(); + if (format.LineAlignment == XLineAlignment.BaseLine) + { + double lineSpace = font.GetHeight(); //old: font.GetHeight(this); + int cellSpace = font.FontFamily.GetLineSpacing(font.Style); + int cellAscent = font.FontFamily.GetCellAscent(font.Style); + int cellDescent = font.FontFamily.GetCellDescent(font.Style); + double cyAscent = lineSpace * cellAscent / cellSpace; + cyAscent = lineSpace * font.CellAscent / font.CellSpace; + rect.Offset(0, (float)-cyAscent); + } + //_gfx.DrawString(text, font.Realize_GdiFont(), brush.RealizeGdiBrush(), rect, + // format != null ? format.RealizeGdiStringFormat() : null); + _gfx.DrawString(text, font.GdiFont, brush.RealizeGdiBrush(), rect, + format.RealizeGdiStringFormat()); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { +#if !SILVERLIGHT + double x = layoutRectangle.X; + double y = layoutRectangle.Y; + + double lineSpace = font.GetHeight(); // old: font.GetHeight(this); + double cyAscent = lineSpace * font.CellAscent / font.CellSpace; + double cyDescent = lineSpace * font.CellDescent / font.CellSpace; + + bool bold = (font.Style & XFontStyle.Bold) != 0; + bool italic = (font.Style & XFontStyle.Italic) != 0; + bool strikeout = (font.Style & XFontStyle.Strikeout) != 0; + bool underline = (font.Style & XFontStyle.Underline) != 0; + + //GlyphRun glyphRun=new GlyphRun(font.GlyphTypeface , 0,); +#if DEBUG_ + if (font.WpfTypeface.FontFamily.Source == "Segoe UI Light") + GetType(); +#endif + FormattedText formattedText = FontHelper.CreateFormattedText(text, font.WpfTypeface, font.Size, brush.RealizeWpfBrush()); + + //formattedText.SetTextDecorations(TextDecorations.OverLine); + switch (format.Alignment) + { + case XStringAlignment.Near: + // nothing to do, this is the default + //formattedText.TextAlignment = TextAlignment.Left; + break; + + case XStringAlignment.Center: + x += layoutRectangle.Width / 2; + formattedText.TextAlignment = TextAlignment.Center; + break; + + case XStringAlignment.Far: + x += layoutRectangle.Width; + formattedText.TextAlignment = TextAlignment.Right; + break; + } + if (PageDirection == XPageDirection.Downwards) + { + switch (format.LineAlignment) + { + case XLineAlignment.Near: + //y += cyAscent; + break; + + case XLineAlignment.Center: + // TODO use CapHeight. PDFlib also uses 3/4 of ascent + y += -formattedText.Baseline + (cyAscent * 1 / 3) + layoutRectangle.Height / 2; + //y += -formattedText.Baseline + (font.Size * font.Metrics.CapHeight / font.unitsPerEm / 2) + layoutRectangle.Height / 2; + break; + + case XLineAlignment.Far: + y += -formattedText.Baseline - cyDescent + layoutRectangle.Height; + break; + + case XLineAlignment.BaseLine: + y -= formattedText.Baseline; + break; + } + } + else + { + // TODOWPF: make unit test + switch (format.LineAlignment) + { + case XLineAlignment.Near: + //y += cyDescent; + break; + + case XLineAlignment.Center: + // TODO use CapHeight. PDFlib also uses 3/4 of ascent + //y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2; + break; + + case XLineAlignment.Far: + //y += -cyAscent + rect.Height; + break; + + case XLineAlignment.BaseLine: + // nothing to do + break; + } + } + + // BoldSimulation and ItalicSimulation is done only in PDF, not in UI. + + if (underline) + { + formattedText.SetTextDecorations(TextDecorations.Underline); + //double underlinePosition = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlinePosition / font.cellSpace; + //double underlineThickness = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlineThickness / font.cellSpace; + //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness); + } + + if (strikeout) + { + formattedText.SetTextDecorations(TextDecorations.Strikethrough); + //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace; + //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace; + //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); + } + + //_dc.DrawText(formattedText, layoutRectangle.Location.ToPoint()); + _dc.DrawText(formattedText, new SysPoint(x, y)); +#else + _dc.DrawString(this, text, font, brush, layoutRectangle, format); +#endif + } +#endif + } + + if (_renderer != null) + _renderer.DrawString(text, font, brush, layoutRectangle, format); + } + + // ----- MeasureString ------------------------------------------------------------------------ + + /// + /// Measures the specified string when drawn with the specified font. + /// + public XSize MeasureString(string text, XFont font, XStringFormat stringFormat) + { + if (text == null) + throw new ArgumentNullException("text"); + if (font == null) + throw new ArgumentNullException("font"); + if (stringFormat == null) + throw new ArgumentNullException("stringFormat"); +#if true + return FontHelper.MeasureString(text, font, stringFormat); +#else + +#if GDI && !WPF + //XSize gdiSize; // #MediumTrust + //if (_gfx != null) + // gdiSize = XSize.FromSizeF(_gfx.MeasureString(text, font.Realize_GdiFont(), new GdiPointF(0, 0), stringFormat.RealizeGdiStringFormat())); + //else + // gdiSize = FontHelper.MeasureString(text, font, XStringFormats.Default); // TODO 4STLA: Why is parameter stringFormat not used here? +#if DEBUG_ + XSize edfSize = FontHelper.MeasureString(text, font, XStringFormats.Default); + //Debug.Assert(gdiSize == edfSize, "Measure string failed."); + if (gdiSize != edfSize) + { + double dx = Math.Abs(gdiSize.Width - edfSize.Width); + double dy = Math.Abs(gdiSize.Height - edfSize.Height); + Debug.Assert(dx < .05 * gdiSize.Width, "MeasureString: width differs."); + Debug.Assert(dy < .05 * gdiSize.Height, "MeasureString: height differs."); + } +#endif + return FontHelper.MeasureString(text, font, XStringFormats.Default); +#endif +#if WPF && !GDI +#if !SILVERLIGHT +#if DEBUG + FormattedText formattedText = FontHelper.CreateFormattedText(text, font.WpfTypeface, font.Size, WpfBrushes.Black); + XSize size1 = FontHelper.MeasureString(text, font, null); + XSize size2 = new XSize(formattedText.WidthIncludingTrailingWhitespace, formattedText.Height); + //Debug.Assert(Math.Abs((size1.Height - size2.Height) * 10) < 1.0); + return size1; +#else + // Same as above, but without code needed for Debug.Assert. + XSize size1 = FontHelper.MeasureString(text, font, null); + return size1; +#endif +#else + // Use the WPF code also for Silverlight. + XSize size1 = FontHelper.MeasureString(text, font, null); + return size1; +#endif + +#endif +#if WPF && GDI +#if true_ + if (TargetContext == XGraphicTargetContext.GDI) + { + XSize gdiSize = XSize.FromSizeF(_gfx.MeasureString(text, font.Realize_GdiFont(), new GdiPointF(0, 0), stringFormat.RealizeGdiStringFormat())); +#if DEBUG +#if GDI + { + //Debug.WriteLine(gdiSize); + XSize edfSize = FontHelper14.MeasureStringGdi(_gfx, text, font, XStringFormats.Default); + //Debug.WriteLine(edfSize); + //Debug.Assert(gdiSize == edfSize, "Measure string failed."); + if (gdiSize.Width != edfSize.Width) + { + Debug.WriteLine(String.Format("Width: {0}, {1} : {2}", gdiSize.Width, edfSize.Width, gdiSize.Width / edfSize.Width)); + } + if (gdiSize.Height != edfSize.Height) + { + Debug.WriteLine(String.Format("Height: {0}, {1}", gdiSize.Height, edfSize.Height)); + } + + //double lineSpace = font.GetHeight(this); + //int cellSpace = font.cellSpace; // font.FontFamily.GetLineSpacing(font.Style); + //int cellAscent = font.cellAscent; // font.FontFamily.GetCellAscent(font.Style); + //int cellDescent = font.cellDescent; // font.FontFamily.GetCellDescent(font.Style); + //double cyAscent = lineSpace * cellAscent / cellSpace; + //double cyDescent = lineSpace * cellDescent / cellSpace; + } +#endif +#if WPF + { + //Debug.WriteLine(gdiSize); + XSize edfSize = FontHelper14.MeasureStringWpf(text, font, XStringFormats.Default); + //Debug.WriteLine(edfSize); + //Debug.Assert(gdiSize == edfSize, "Measure string failed."); + if (gdiSize.Width != edfSize.Width) + { + Debug.WriteLine(String.Format("Width: {0}, {1} : {2}", gdiSize.Width, edfSize.Width, gdiSize.Width / edfSize.Width)); + } + if (gdiSize.Height != edfSize.Height) + { + Debug.WriteLine(String.Format("Height: {0}, {1}", gdiSize.Height, edfSize.Height)); + } + + //double lineSpace = font.GetHeight(this); + //int cellSpace = font.cellSpace; // font.FontFamily.GetLineSpacing(font.Style); + //int cellAscent = font.cellAscent; // font.FontFamily.GetCellAscent(font.Style); + //int cellDescent = font.cellDescent; // font.FontFamily.GetCellDescent(font.Style); + //double cyAscent = lineSpace * cellAscent / cellSpace; + //double cyDescent = lineSpace * cellDescent / cellSpace; + } +#endif +#endif + return gdiSize; + } + if (TargetContext == XGraphicTargetContext.WPF) + { + //double h = font.Height; + //FormattedText formattedText = new FormattedText(text, new CultureInfo("en-us"), + // FlowDirection.LeftToRight, font.typeface, font.Size, WpfBrushes.Black); + FormattedText formattedText = FontHelper.CreateFormattedText(text, font.Typeface, font.Size, WpfBrushes.Black); + XSize wpfSize = new XSize(formattedText.WidthIncludingTrailingWhitespace, formattedText.Height); +#if DEBUG + Debug.WriteLine(wpfSize); +#endif + return wpfSize; + } + Debug.Assert(false); + return XSize.Empty; +#else + XSize size23 = FontHelper.MeasureString(text, font, XStringFormats.Default); + return size23; +#endif +#endif +#if CORE || NETFX_CORE || UWP || DNC10 + XSize size = FontHelper.MeasureString(text, font, XStringFormats.Default); + return size; +#endif +#endif + } + + /// + /// Measures the specified string when drawn with the specified font. + /// + public XSize MeasureString(string text, XFont font) + { + return MeasureString(text, font, XStringFormats.Default); + } + + // ----- DrawImage ---------------------------------------------------------------------------- + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, GdiPoint point) + { + DrawImage(image, point.X, point.Y); + } +#endif + +#if WPF + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, SysPoint point) + { + DrawImage(image, point.X, point.Y); + } +#endif + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, GdiPointF point) + { + DrawImage(image, point.X, point.Y); + } +#endif + + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, XPoint point) + { + DrawImage(image, point.X, point.Y); + } + + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, double x, double y) + { + if (image == null) + throw new ArgumentNullException("image"); + + CheckXPdfFormConsistence(image); + + double width = image.PointWidth; + double height = image.PointHeight; + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (image._gdiImage != null) + { + InterpolationMode interpolationMode = InterpolationMode.Invalid; + if (!image.Interpolate) + { + interpolationMode = _gfx.InterpolationMode; + _gfx.InterpolationMode = InterpolationMode.NearestNeighbor; + } + + _gfx.DrawImage(image._gdiImage, (float)x, (float)y, (float)width, (float)height); + + if (!image.Interpolate) + _gfx.InterpolationMode = interpolationMode; + } + else + { + DrawMissingImageRect(new XRect(x, y, width, height)); + //_gfx.DrawRectangle(Pens.Red, (float)x, (float)y, (float)width, (float)height); + //_gfx.DrawLine(Pens.Red, (float)x, (float)y, (float)(x + width), (float)(y + height)); + //_gfx.DrawLine(Pens.Red, (float)(x + width), (float)y, (float)x, (float)(y + height)); + } + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + if (image._wpfImage != null) + { + _dc.DrawImage(image._wpfImage, new Rect(x, y, image.PointWidth, image.PointHeight)); + } + else + { + DrawMissingImageRect(new XRect(x, y, width, height)); + } + } +#endif + } + + if (_renderer != null) + _renderer.DrawImage(image, x, y, image.PointWidth, image.PointHeight); + //image.Width * 72 / image.HorizontalResolution, + //image.Height * 72 / image.HorizontalResolution); + } + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, Rectangle rect) + { + // Because of overloading the cast is NOT redundant. + DrawImage(image, (double)rect.X, (double)rect.Y, (double)rect.Width, (double)rect.Height); + } +#endif + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, GdiRectF rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, XRect rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, double x, double y, double width, double height) + { + if (image == null) + throw new ArgumentNullException("image"); + + CheckXPdfFormConsistence(image); + + if (_drawGraphics) + { + // THHO4STLA: Platform-independent images cannot be drawn here, can they? => They can. Lazy create platform-dependent image and draw that. +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (image._gdiImage != null) + { + InterpolationMode interpolationMode = InterpolationMode.Invalid; + if (!image.Interpolate) + { + interpolationMode = _gfx.InterpolationMode; + _gfx.InterpolationMode = InterpolationMode.NearestNeighbor; + } + + _gfx.DrawImage(image._gdiImage, (float)x, (float)y, (float)width, (float)height); + + if (!image.Interpolate) + _gfx.InterpolationMode = interpolationMode; + } + else + { + XImage placeholder = null; + XPdfForm pdfForm = image as XPdfForm; + if (pdfForm != null) + { + //XPdfForm pf = pdfForm; + if (pdfForm.PlaceHolder != null) + placeholder = pdfForm.PlaceHolder; + } + if (placeholder != null) + _gfx.DrawImage(placeholder._gdiImage, (float)x, (float)y, (float)width, + (float)height); + else + { + DrawMissingImageRect(new XRect(x, y, width, height)); + } + } + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + if (image._wpfImage != null) + { + //InterpolationMode interpolationMode = InterpolationMode.Invalid; + //if (!image.Interpolate) + //{ + // interpolationMode = gfx.InterpolationMode; + // gfx.InterpolationMode = InterpolationMode.NearestNeighbor; + //} + + _dc.DrawImage(image._wpfImage, new Rect(x, y, width, height)); + + //if (!image.Interpolate) + // gfx.InterpolationMode = interpolationMode; + } + else + { + XImage placeholder = null; + if (image is XPdfForm) + { + XPdfForm pf = image as XPdfForm; + if (pf.PlaceHolder != null) + placeholder = pf.PlaceHolder; + } + if (placeholder != null) + _dc.DrawImage(placeholder._wpfImage, new Rect(x, y, width, height)); + else + DrawMissingImageRect(new XRect(x, y, width, height)); + } + } +#endif + } + + if (_renderer != null) + _renderer.DrawImage(image, x, y, width, height); + } + + // TODO: calculate destination size + //public void DrawImage(XImage image, double x, double y, GdiRectF srcRect, XGraphicsUnit srcUnit) + //public void DrawImage(XImage image, double x, double y, XRect srcRect, XGraphicsUnit srcUnit) + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, Rectangle destRect, Rectangle srcRect, XGraphicsUnit srcUnit) + { + XRect destRectX = new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height); + XRect srcRectX = new XRect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height); + DrawImage(image, destRectX, srcRectX, srcUnit); + } +#endif + +#if GDI + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, GdiRectF destRect, GdiRectF srcRect, XGraphicsUnit srcUnit) + { + XRect destRectX = new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height); + XRect srcRectX = new XRect(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height); + DrawImage(image, destRectX, srcRectX, srcUnit); + } +#endif + + /// + /// Draws the specified image. + /// + public void DrawImage(XImage image, XRect destRect, XRect srcRect, XGraphicsUnit srcUnit) + { + if (image == null) + throw new ArgumentNullException("image"); + + CheckXPdfFormConsistence(image); + + if (_drawGraphics) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + if (image._gdiImage != null) + { + InterpolationMode interpolationMode = InterpolationMode.Invalid; + if (!image.Interpolate) + { + interpolationMode = _gfx.InterpolationMode; + _gfx.InterpolationMode = InterpolationMode.NearestNeighbor; + } + + GdiRectF destRectF = new GdiRectF((float)destRect.X, (float)destRect.Y, + (float)destRect.Width, (float)destRect.Height); + GdiRectF srcRectF = new GdiRectF((float)srcRect.X, (float)srcRect.Y, + (float)srcRect.Width, (float)srcRect.Height); + _gfx.DrawImage(image._gdiImage, destRectF, srcRectF, GraphicsUnit.Pixel); + + if (!image.Interpolate) + _gfx.InterpolationMode = interpolationMode; + } + else + { + DrawMissingImageRect(new XRect(destRect.X, destRect.Y, destRect.Width, destRect.Height)); + } + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + if (image._wpfImage != null) + { + //InterpolationMode interpolationMode = InterpolationMode.Invalid; + //if (!image.Interpolate) + //{ + // interpolationMode = gfx.InterpolationMode; + // //gfx.InterpolationMode = InterpolationMode.NearestNeighbor; + //} + + // HACK: srcRect is ignored + //double x = destRect.X; + //double y = destRect.Y; + _dc.DrawImage(image._wpfImage, new SysRect(destRect.X, destRect.Y, destRect.Width, destRect.Height)); + + //if (!image.Interpolate) + // gfx.InterpolationMode = interpolationMode; + } + else + { + DrawMissingImageRect(destRect); + } + } +#endif + } + + if (_renderer != null) + _renderer.DrawImage(image, destRect, srcRect, srcUnit); + } + + //TODO? + //public void DrawImage(XImage image, Rectangle destRect, double srcX, double srcY, double srcWidth, double srcHeight, GraphicsUnit srcUnit); + //public void DrawImage(XImage image, Rectangle destRect, double srcX, double srcY, double srcWidth, double srcHeight, GraphicsUnit srcUnit); + + void DrawMissingImageRect(XRect rect) + { +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + float x = (float)rect.X; + float y = (float)rect.Y; + float width = (float)rect.Width; + float height = (float)rect.Height; + _gfx.DrawRectangle(Pens.Red, x, y, width, height); + _gfx.DrawLine(Pens.Red, x, y, x + width, y + height); + _gfx.DrawLine(Pens.Red, x + width, y, x, y + height); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + double x = rect.X; + double y = rect.Y; + double width = rect.Width; + double height = rect.Height; +#if !SILVERLIGHT + WpfPen pen = new WpfPen(WpfBrushes.Red, 1); +#else + WpfPen pen = new WpfPen(); + pen.Brush = new SolidColorBrush(Colors.Red); + pen.Thickness = 1; +#endif + _dc.DrawRectangle(null, pen, new Rect(x, y, width, height)); + _dc.DrawLine(pen, new SysPoint(x, y), new SysPoint(x + width, y + height)); + _dc.DrawLine(pen, new SysPoint(x + width, y), new SysPoint(x, y + height)); + } +#endif + } + + /// + /// Checks whether drawing is allowed and disposes the XGraphics object, if necessary. + /// + void CheckXPdfFormConsistence(XImage image) + { + XForm xForm = image as XForm; + if (xForm != null) + { + // Force disposing of XGraphics that draws the content + xForm.Finish(); + + // ReSharper disable once MergeSequentialChecks + if (_renderer != null && (_renderer as XGraphicsPdfRenderer) != null) + { + if (xForm.Owner != null && xForm.Owner != ((XGraphicsPdfRenderer)_renderer).Owner) + throw new InvalidOperationException( + "A XPdfForm object is always bound to the document it was created for and cannot be drawn in the context of another document."); + + if (xForm == ((XGraphicsPdfRenderer)_renderer)._form) + throw new InvalidOperationException( + "A XPdfForm cannot be drawn on itself."); + } + } + } + + // ----- DrawBarCode -------------------------------------------------------------------------- + + /// + /// Draws the specified bar code. + /// + public void DrawBarCode(BarCodes.BarCode barcode, XPoint position) + { + barcode.Render(this, XBrushes.Black, null, position); + } + + /// + /// Draws the specified bar code. + /// + public void DrawBarCode(BarCodes.BarCode barcode, XBrush brush, XPoint position) + { + barcode.Render(this, brush, null, position); + } + + /// + /// Draws the specified bar code. + /// + public void DrawBarCode(BarCodes.BarCode barcode, XBrush brush, XFont font, XPoint position) + { + barcode.Render(this, brush, font, position); + } + + // ----- DrawMatrixCode ----------------------------------------------------------------------- + + /// + /// Draws the specified data matrix code. + /// + public void DrawMatrixCode(BarCodes.MatrixCode matrixcode, XPoint position) + { + matrixcode.Render(this, XBrushes.Black, position); + } + + /// + /// Draws the specified data matrix code. + /// + public void DrawMatrixCode(BarCodes.MatrixCode matrixcode, XBrush brush, XPoint position) + { + matrixcode.Render(this, brush, position); + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Save and Restore + + /// + /// Saves the current state of this XGraphics object and identifies the saved state with the + /// returned XGraphicsState object. + /// + public XGraphicsState Save() + { + XGraphicsState xState = null; +#if CORE || NETFX_CORE + if (TargetContext == XGraphicTargetContext.CORE || TargetContext == XGraphicTargetContext.NONE) + { + xState = new XGraphicsState(); + InternalGraphicsState iState = new InternalGraphicsState(this, xState); + iState.Transform = _transform; + _gsStack.Push(iState); + } + else + { + Debug.Assert(false, "XGraphicTargetContext must be XGraphicTargetContext.CORE."); + } +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + xState = new XGraphicsState(_gfx != null ? _gfx.Save() : null); + InternalGraphicsState iState = new InternalGraphicsState(this, xState); + iState.Transform = _transform; + _gsStack.Push(iState); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + xState = new XGraphicsState(); + InternalGraphicsState iState = new InternalGraphicsState(this, xState); + iState.Transform = _transform; + _gsStack.Push(iState); + } +#endif + + if (_renderer != null) + _renderer.Save(xState); + + return xState; + } + + /// + /// Restores the state of this XGraphics object to the state represented by the specified + /// XGraphicsState object. + /// + public void Restore(XGraphicsState state) + { + if (state == null) + throw new ArgumentNullException("state"); + +#if CORE + if (TargetContext == XGraphicTargetContext.CORE) + { + _gsStack.Restore(state.InternalState); + _transform = state.InternalState.Transform; + } +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gsStack.Restore(state.InternalState); + if (_gfx != null) + _gfx.Restore(state.GdiState); + _transform = state.InternalState.Transform; + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { + _gsStack.Restore(state.InternalState); + _transform = state.InternalState.Transform; + } +#endif + + if (_renderer != null) + _renderer.Restore(state); + } + + /// + /// Restores the state of this XGraphics object to the state before the most recently call of Save. + /// + public void Restore() + { + if (_gsStack.Count == 0) + throw new InvalidOperationException("Cannot restore without preceding save operation."); + Restore(_gsStack.Current.State); + } + + /// + /// Saves a graphics container with the current state of this XGraphics and + /// opens and uses a new graphics container. + /// + public XGraphicsContainer BeginContainer() + { + return BeginContainer(new XRect(0, 0, 1, 1), new XRect(0, 0, 1, 1), XGraphicsUnit.Point); + } + +#if GDI + /// + /// Saves a graphics container with the current state of this XGraphics and + /// opens and uses a new graphics container. + /// + public XGraphicsContainer BeginContainer(Rectangle dstrect, Rectangle srcrect, XGraphicsUnit unit) + { + return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit); + } +#endif + +#if GDI + /// + /// Saves a graphics container with the current state of this XGraphics and + /// opens and uses a new graphics container. + /// + public XGraphicsContainer BeginContainer(GdiRectF dstrect, GdiRectF srcrect, XGraphicsUnit unit) + { + return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit); + } +#endif + +#if WPF + /// + /// Saves a graphics container with the current state of this XGraphics and + /// opens and uses a new graphics container. + /// + public XGraphicsContainer BeginContainer(Rect dstrect, Rect srcrect, XGraphicsUnit unit) + { + return BeginContainer(new XRect(dstrect), new XRect(dstrect), unit); + } +#endif + + /// + /// Saves a graphics container with the current state of this XGraphics and + /// opens and uses a new graphics container. + /// + public XGraphicsContainer BeginContainer(XRect dstrect, XRect srcrect, XGraphicsUnit unit) + { + // TODO: unit + if (unit != XGraphicsUnit.Point) + throw new ArgumentException("The current implementation supports XGraphicsUnit.Point only.", "unit"); + + XGraphicsContainer xContainer = null; +#if CORE + if (TargetContext == XGraphicTargetContext.CORE) + xContainer = new XGraphicsContainer(); +#endif +#if GDI + // _gfx can be null if drawing applies to PDF page only. + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + xContainer = new XGraphicsContainer(_gfx != null ? _gfx.Save() : null); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + xContainer = new XGraphicsContainer(); +#endif + InternalGraphicsState iState = new InternalGraphicsState(this, xContainer); + iState.Transform = _transform; + + _gsStack.Push(iState); + + if (_renderer != null) + _renderer.BeginContainer(xContainer, dstrect, srcrect, unit); + + XMatrix matrix = new XMatrix(); + double scaleX = dstrect.Width / srcrect.Width; + double scaleY = dstrect.Height / srcrect.Height; + matrix.TranslatePrepend(-srcrect.X, -srcrect.Y); + matrix.ScalePrepend(scaleX, scaleY); + matrix.TranslatePrepend(dstrect.X / scaleX, dstrect.Y / scaleY); + AddTransform(matrix, XMatrixOrder.Prepend); + + return xContainer; + } + + /// + /// Closes the current graphics container and restores the state of this XGraphics + /// to the state saved by a call to the BeginContainer method. + /// + public void EndContainer(XGraphicsContainer container) + { + if (container == null) + throw new ArgumentNullException("container"); + + _gsStack.Restore(container.InternalState); +#if CORE + // nothing to do +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI && _gfx != null) + { + try + { + Lock.EnterGdiPlus(); + _gfx.Restore(container.GdiState); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + // nothing to do +#endif + _transform = container.InternalState.Transform; + + if (_renderer != null) + _renderer.EndContainer(container); + } + + /// + /// Gets the current graphics state level. The default value is 0. Each call of Save or BeginContainer + /// increased and each call of Restore or EndContainer decreased the value by 1. + /// + public int GraphicsStateLevel + { + get { return _gsStack.Count; } + } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Properties + + /// + /// Gets or sets the smoothing mode. + /// + /// The smoothing mode. + public XSmoothingMode SmoothingMode + { + get + { +#if CORE + // nothing to do +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI && + _gfx != null) + { + try + { + Lock.EnterGdiPlus(); + return (XSmoothingMode)_gfx.SmoothingMode; + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + // nothing to do +#endif + return _smoothingMode; + } + set + { + _smoothingMode = value; +#if CORE + // nothing to do +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI && + _gfx != null) + { + try + { + Lock.EnterGdiPlus(); + _gfx.SmoothingMode = (SmoothingMode)value; + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + // nothing to do +#endif + } + } + XSmoothingMode _smoothingMode; + + //public Region Clip { get; set; } + //public GdiRectF ClipBounds { get; } + //public CompositingMode CompositingMode { get; set; } + //public CompositingQuality CompositingQuality { get; set; } + //public float DpiX { get; } + //public float DpiY { get; } + //public InterpolationMode InterpolationMode { get; set; } + //public bool IsClipEmpty { get; } + //public bool IsVisibleClipEmpty { get; } + //public float PageScale { get; set; } + //public GraphicsUnit PageUnit { get; set; } + //public PixelOffsetMode PixelOffsetMode { get; set; } + //public Point RenderingOrigin { get; set; } + //public SmoothingMode SmoothingMode { get; set; } + //public int TextContrast { get; set; } + //public TextRenderingHint TextRenderingHint { get; set; } + //public Matrix Transform { get; set; } + //public GdiRectF VisibleClipBounds { get; } + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Transformation + + /// + /// Applies the specified translation operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void TranslateTransform(double dx, double dy) + { + AddTransform(XMatrix.CreateTranslation(dx, dy), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified translation operation to the transformation matrix of this object + /// in the specified order. + /// + public void TranslateTransform(double dx, double dy, XMatrixOrder order) + { + XMatrix matrix = new XMatrix(); + matrix.TranslatePrepend(dx, dy); + AddTransform(matrix, order); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void ScaleTransform(double scaleX, double scaleY) + { + AddTransform(XMatrix.CreateScaling(scaleX, scaleY), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object + /// in the specified order. + /// + public void ScaleTransform(double scaleX, double scaleY, XMatrixOrder order) + { + XMatrix matrix = new XMatrix(); + matrix.ScalePrepend(scaleX, scaleY); + AddTransform(matrix, order); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + // ReSharper disable once InconsistentNaming + public void ScaleTransform(double scaleXY) + { + ScaleTransform(scaleXY, scaleXY); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object + /// in the specified order. + /// + // ReSharper disable once InconsistentNaming + public void ScaleTransform(double scaleXY, XMatrixOrder order) + { + ScaleTransform(scaleXY, scaleXY, order); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void ScaleAtTransform(double scaleX, double scaleY, double centerX, double centerY) + { + AddTransform(XMatrix.CreateScaling(scaleX, scaleY, centerX, centerY), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified scaling operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void ScaleAtTransform(double scaleX, double scaleY, XPoint center) + { + AddTransform(XMatrix.CreateScaling(scaleX, scaleY, center.X, center.Y), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified rotation operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void RotateTransform(double angle) + { + AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified rotation operation to the transformation matrix of this object + /// in the specified order. The angle unit of measure is degree. + /// + public void RotateTransform(double angle, XMatrixOrder order) + { + XMatrix matrix = new XMatrix(); + matrix.RotatePrepend(angle); + AddTransform(matrix, order); + } + + /// + /// Applies the specified rotation operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void RotateAtTransform(double angle, XPoint point) + { + AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified rotation operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// + public void RotateAtTransform(double angle, XPoint point, XMatrixOrder order) + { + AddTransform(XMatrix.CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y), order); + } + + /// + /// Applies the specified shearing operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// ShearTransform is a synonym for SkewAtTransform. + /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis. + /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis. + /// + public void ShearTransform(double shearX, double shearY) + { + AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified shearing operation to the transformation matrix of this object + /// in the specified order. + /// ShearTransform is a synonym for SkewAtTransform. + /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis. + /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis. + /// + public void ShearTransform(double shearX, double shearY, XMatrixOrder order) + { + AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad), order); + } + + /// + /// Applies the specified shearing operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// ShearTransform is a synonym for SkewAtTransform. + /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis. + /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis. + /// + public void SkewAtTransform(double shearX, double shearY, double centerX, double centerY) + { + AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad, centerX, centerY), XMatrixOrder.Prepend); + } + + /// + /// Applies the specified shearing operation to the transformation matrix of this object by + /// prepending it to the object's transformation matrix. + /// ShearTransform is a synonym for SkewAtTransform. + /// Parameter shearX specifies the horizontal skew which is measured in degrees counterclockwise from the y-axis. + /// Parameter shearY specifies the vertical skew which is measured in degrees counterclockwise from the x-axis. + /// + public void SkewAtTransform(double shearX, double shearY, XPoint center) + { + AddTransform(XMatrix.CreateSkewRadians(shearX * Const.Deg2Rad, shearY * Const.Deg2Rad, center.X, center.Y), XMatrixOrder.Prepend); + } + + /// + /// Multiplies the transformation matrix of this object and specified matrix. + /// + public void MultiplyTransform(XMatrix matrix) + { + AddTransform(matrix, XMatrixOrder.Prepend); + } + + /// + /// Multiplies the transformation matrix of this object and specified matrix in the specified order. + /// + public void MultiplyTransform(XMatrix matrix, XMatrixOrder order) + { + AddTransform(matrix, order); + } + + /// + /// Gets the current transformation matrix. + /// The transformation matrix cannot be set. Instead use Save/Restore or BeginContainer/EndContainer to + /// save the state before Transform is called and later restore to the previous transform. + /// + public XMatrix Transform + { + get { return _transform; } + } + + /// + /// Applies a new transformation to the current transformation matrix. + /// + void AddTransform(XMatrix transform, XMatrixOrder order) + { + XMatrix matrix = _transform; + matrix.Multiply(transform, order); + _transform = matrix; + matrix = DefaultViewMatrix; + matrix.Multiply(_transform, XMatrixOrder.Prepend); +#if CORE + if (TargetContext == XGraphicTargetContext.CORE) + { + GetType(); + // TODO: _gsStack... + } +#endif +#if GDI + if (TargetContext == XGraphicTargetContext.GDI) + { + if (_gfx != null) + { + try + { + Lock.EnterGdiPlus(); + _gfx.Transform = (GdiMatrix)matrix; + } + finally { Lock.ExitGdiPlus(); } + } + } +#endif +#if WPF + if (TargetContext == XGraphicTargetContext.WPF) + { +#if !SILVERLIGHT + MatrixTransform mt = new MatrixTransform(transform.ToWpfMatrix()); +#else + MatrixTransform mt = new MatrixTransform(); + mt.Matrix = transform.ToWpfMatrix(); +#endif + if (order == XMatrixOrder.Append) + mt = (MatrixTransform)mt.Inverse; + _gsStack.Current.PushTransform(mt); + } +#endif + if (_renderer != null) + _renderer.AddTransform(transform, XMatrixOrder.Prepend); + } + + //public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] points) + //{ + //} + // + //public void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, GdiPointF[] points) + //{ + //} + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Clipping + +#if GDI + /// + /// Updates the clip region of this XGraphics to the intersection of the + /// current clip region and the specified rectangle. + /// + public void IntersectClip(Rectangle rect) + { + XGraphicsPath path = new XGraphicsPath(); + path.AddRectangle(rect); + IntersectClip(path); + } +#endif + +#if GDI + /// + /// Updates the clip region of this XGraphics to the intersection of the + /// current clip region and the specified rectangle. + /// + public void IntersectClip(GdiRectF rect) + { + XGraphicsPath path = new XGraphicsPath(); + path.AddRectangle(rect); + IntersectClip(path); + } +#endif + + /// + /// Updates the clip region of this XGraphics to the intersection of the + /// current clip region and the specified rectangle. + /// + public void IntersectClip(XRect rect) + { + XGraphicsPath path = new XGraphicsPath(); + path.AddRectangle(rect); + IntersectClip(path); + } + + /// + /// Updates the clip region of this XGraphics to the intersection of the + /// current clip region and the specified graphical path. + /// + public void IntersectClip(XGraphicsPath path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (_drawGraphics) + { +#if GDI && !WPF + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.SetClip(path._gdipPath, CombineMode.Intersect); + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF && !GDI + if (TargetContext == XGraphicTargetContext.WPF) + _gsStack.Current.PushClip(path._pathGeometry); +#endif +#if GDI && WPF + if (TargetContext == XGraphicTargetContext.GDI) + { + try + { + Lock.EnterGdiPlus(); + _gfx.SetClip(path._gdipPath, CombineMode.Intersect); + } + finally { Lock.ExitGdiPlus(); } + } + else + { + _gsStack.Current.PushClip(path._pathGeometry); + } +#endif + } + + if (_renderer != null) + _renderer.SetClip(path, XCombineMode.Intersect); + } + + //public void SetClip(Graphics g); + //public void SetClip(Graphics g, CombineMode combineMode); + //public void SetClip(GraphicsPath path, CombineMode combineMode); + //public void SetClip(Rectangle rect, CombineMode combineMode); + //public void SetClip(GdiRectF rect, CombineMode combineMode); + //public void SetClip(Region region, CombineMode combineMode); + //public void IntersectClip(Region region); + //public void ExcludeClip(Region region); + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Miscellaneous + + /// + /// Writes a comment to the output stream. Comments have no effect on the rendering of the output. + /// They may be useful to mark a position in a content stream of a PDF document. + /// + public void WriteComment(string comment) + { + if (comment == null) + throw new ArgumentNullException("comment"); + + if (_drawGraphics) + { + // TODO: Do something if metafile? + } + + if (_renderer != null) + _renderer.WriteComment(comment); + } + + /// + /// Permits access to internal data. + /// + public XGraphicsInternals Internals + { + get { return _internals ?? (_internals = new XGraphicsInternals(this)); } + } + XGraphicsInternals _internals; + + /// + /// (Under construction. May change in future versions.) + /// + public SpaceTransformer Transformer + { + get { return _transformer ?? (_transformer = new SpaceTransformer(this)); } + } + SpaceTransformer _transformer; + + #endregion + + // -------------------------------------------------------------------------------------------- + + #region Internal Helper Functions + +#if GDI + /// + /// Converts a GdiPoint[] into a GdiPointF[]. + /// + internal static GdiPointF[] MakePointFArray(GdiPoint[] points, int offset, int count) + { + if (points == null) + return null; + + //int length = points.Length; + GdiPointF[] result = new GdiPointF[count]; + for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++) + { + result[idx].X = points[srcIdx].X; + result[idx].Y = points[srcIdx].Y; + } + return result; + } +#endif + +#if GDI + /// + /// Converts a XPoint[] into a GdiPointF[]. + /// + internal static GdiPointF[] MakePointFArray(XPoint[] points) + { + if (points == null) + return null; + + int count = points.Length; + GdiPointF[] result = new GdiPointF[count]; + for (int idx = 0; idx < count; idx++) + { + result[idx].X = (float)points[idx].X; + result[idx].Y = (float)points[idx].Y; + } + return result; + } +#endif + +#if GDI + /// + /// Converts a Point[] into a XPoint[]. + /// + internal static XPoint[] MakeXPointArray(GdiPoint[] points, int offset, int count) + { + if (points == null) + return null; + + //int lengh = points.Length; + XPoint[] result = new XPoint[count]; + for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++) + { + result[idx].X = points[srcIdx].X; + result[idx].Y = points[srcIdx].Y; + } + return result; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Converts a Point[] into a XPoint[]. + /// + internal static XPoint[] MakeXPointArray(SysPoint[] points, int offset, int count) + { + if (points == null) + return null; + + //int length = points.Length; + XPoint[] result = new XPoint[count]; + for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++) + { + result[idx].X = points[srcIdx].X; + result[idx].Y = points[srcIdx].Y; + } + return result; + } +#endif + +#if GDI + /// + /// Converts a GdiPointF[] into a XPoint[]. + /// + internal static XPoint[] MakeXPointArray(GdiPointF[] points, int offset, int count) + { + if (points == null) + return null; + + //int length = points.Length; + XPoint[] result = new XPoint[count]; + for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++) + { + result[idx].X = points[srcIdx].X; + result[idx].Y = points[srcIdx].Y; + } + return result; + } +#endif + +#if GDI + /// + /// Converts a XRect[] into a GdiRectF[]. + /// + internal static GdiRectF[] MakeRectangleFArray(XRect[] rects, int offset, int count) + { + if (rects == null) + return null; + + //int length = rects.Length; + GdiRectF[] result = new GdiRectF[count]; + for (int idx = 0, srcIdx = offset; idx < count; idx++, srcIdx++) + { + XRect rect = rects[srcIdx]; + result[idx] = new GdiRectF((float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height); + } + return result; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Converts an XPoint[] into a Point[]. + /// + internal static SysPoint[] MakePointArray(XPoint[] points) + { + if (points == null) + return null; + + int count = points.Length; + SysPoint[] result = new SysPoint[count]; + for (int idx = 0; idx < count; idx++) + { + result[idx].X = points[idx].X; + result[idx].Y = points[idx].Y; + } + return result; + } +#endif + + #endregion + + ///// + ///// Testcode + ///// + //public void TestXObject(PdfDocument thisDoc, PdfPage thisPage, int page, + // PdfDocument externalDoc, ImportedObjectTable impDoc) + //{ + // PdfPage impPage = externalDoc.Pages[page]; + // // impDoc.ImportPage(impPage); + // PdfFormXObject form = new PdfFormXObject(thisDoc, impDoc, impPage); + // thisDoc.xrefTable.Add(form); + + // PdfDictionary xobjects = new PdfDictionary(); + // xobjects.Elements["/X42"] = form.XRef; + // thisPage.Resources.Elements[PdfResources.Keys.XObject] = xobjects; + // ((XGraphicsPdfRenderer)renderer).DrawXObject("/X42"); + //} + + internal void DisassociateImage() + { + if (_associatedImage == null) + throw new InvalidOperationException("No image associated."); + + Dispose(); + } + + internal InternalGraphicsMode InternalGraphicsMode + { + get { return _internalGraphicsMode; } + set { _internalGraphicsMode = value; } + } + InternalGraphicsMode _internalGraphicsMode; + + internal XImage AssociatedImage + { + get { return _associatedImage; } + set { _associatedImage = value; } + } + XImage _associatedImage; + +#if GDI + /// + /// Always defined System.Drawing.Graphics object. Used as 'query context' for PDF pages. + /// + internal Graphics _gfx; +#endif + +#if WPF + /// + /// Always defined System.Drawing.Graphics object. Used as 'query context' for PDF pages. + /// +#if !SILVERLIGHT + DrawingVisual _dv; + internal DrawingContext _dc; +#else + internal AgDrawingContext _dc; +#endif +#endif + +#if UWP + readonly CanvasDrawingSession _cds; +#endif + + /// + /// The transformation matrix from the XGraphics page space to the Graphics world space. + /// (The name 'default view matrix' comes from Microsoft OS/2 Presentation Manager. I choose + /// this name because I have no better one.) + /// + internal XMatrix DefaultViewMatrix; + + /// + /// Indicates whether to send drawing operations to _gfx or _dc. + /// + bool _drawGraphics; + + readonly XForm _form; + +#if GDI + internal Metafile Metafile; +#endif + + /// + /// Interface to an (optional) renderer. Currently it is the XGraphicsPdfRenderer, if defined. + /// + internal IXGraphicsRenderer _renderer; + + // @PDF/UA + internal void AppendToContentStream(string str) + { + XGraphicsPdfRenderer r = _renderer as XGraphicsPdfRenderer; + if (r != null) + r.Append(str); + } + + /// + /// The transformation matrix from XGraphics world space to page unit space. + /// + XMatrix _transform; + + /// + /// The graphics state stack. + /// + readonly GraphicsStateStack _gsStack; + + /// + /// Gets the PDF page that serves as drawing surface if PDF is rendered, + /// or null, if no such object exists. + /// + public PdfPage PdfPage + { + get + { + XGraphicsPdfRenderer renderer = _renderer as PdfSharp.Drawing.Pdf.XGraphicsPdfRenderer; + return renderer != null ? renderer._page : null; + } + } + +#if GDI + /// + /// Gets the System.Drawing.Graphics objects that serves as drawing surface if no PDF is rendered, + /// or null, if no such object exists. + /// + public Graphics Graphics + { + get { return _gfx; } + } +#endif + + //#if CORE || GDI + // /// + // /// Critical section used to serialize access to GDI+. + // /// This may be necessary to use PDFsharp safely in a Web application. + // /// + // internal static readonly object GdiPlus = new object(); + //#endif + + /// + /// Provides access to internal data structures of the XGraphics class. + /// + public class XGraphicsInternals + { + internal XGraphicsInternals(XGraphics gfx) + { + _gfx = gfx; + } + readonly XGraphics _gfx; + +#if GDI + /// + /// Gets the underlying Graphics object. + /// + public Graphics Graphics + { + get { return _gfx._gfx; } + } +#endif + + /// + /// Gets the content string builder of XGraphicsPdfRenderer, if it exists. + /// + public StringBuilder ContentStringBuilder + { + get + { + XGraphicsPdfRenderer renderer = _gfx._renderer as XGraphicsPdfRenderer; + if (renderer != null) + return renderer._content; + return null; + } + } + } + + /// + /// (This class is under construction.) + /// Currently used in MigraDoc + /// + public class SpaceTransformer + { + internal SpaceTransformer(XGraphics gfx) + { + _gfx = gfx; + } + readonly XGraphics _gfx; + + /// + /// Gets the smallest rectangle in default page space units that completely encloses the specified rect + /// in world space units. + /// + public XRect WorldToDefaultPage(XRect rect) + { + XPoint[] points = new XPoint[4]; + points[0] = new XPoint(rect.X, rect.Y); + points[1] = new XPoint(rect.X + rect.Width, rect.Y); + points[2] = new XPoint(rect.X, rect.Y + rect.Height); + points[3] = new XPoint(rect.X + rect.Width, rect.Y + rect.Height); + + XMatrix matrix = _gfx.Transform; + matrix.TransformPoints(points); + + double height = _gfx.PageSize.Height; + points[0].Y = height - points[0].Y; + points[1].Y = height - points[1].Y; + points[2].Y = height - points[2].Y; + points[3].Y = height - points[3].Y; + + double xmin = Math.Min(Math.Min(points[0].X, points[1].X), Math.Min(points[2].X, points[3].X)); + double xmax = Math.Max(Math.Max(points[0].X, points[1].X), Math.Max(points[2].X, points[3].X)); + double ymin = Math.Min(Math.Min(points[0].Y, points[1].Y), Math.Min(points[2].Y, points[3].Y)); + double ymax = Math.Max(Math.Max(points[0].Y, points[1].Y), Math.Max(points[2].Y, points[3].Y)); + + return new XRect(xmin, ymin, xmax - xmin, ymax - ymin); + } + + /// + /// Gets a point in PDF world space units. + /// + public XPoint WorldToDefaultPage(XPoint point) + { + XMatrix matrix = _gfx.Transform; + matrix.Transform(point); + + double height = _gfx.PageSize.Height; + point.Y = height - point.Y; + + return point; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsContainer.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsContainer.cs new file mode 100644 index 00000000..206a6d38 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsContainer.cs @@ -0,0 +1,58 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents the internal state of an XGraphics object. + /// + public sealed class XGraphicsContainer + { +#if GDI + internal XGraphicsContainer(GraphicsState state) + { + GdiState = state; + } + internal GraphicsState GdiState; +#endif +#if WPF + internal XGraphicsContainer() + { } +#endif + internal InternalGraphicsState InternalState; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPath.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPath.cs new file mode 100644 index 00000000..52ffda4b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPath.cs @@ -0,0 +1,2289 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +#if !SILVERLIGHT +using WpfBrushes = System.Windows.Media.Brushes; +#endif +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +using SysRect = Windows.Foundation.Rect; +#endif +using PdfSharp.Internal; + +namespace PdfSharp.Drawing +{ + /// + /// Represents a series of connected lines and curves. + /// + public sealed class XGraphicsPath + { + /// + /// Initializes a new instance of the class. + /// + public XGraphicsPath() + { +#if CORE + _corePath = new CoreGraphicsPath(); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath = new GraphicsPath(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + _pathGeometry = new PathGeometry(); +#endif + } + +#if GDI + /// + /// Initializes a new instance of the class. + /// + public XGraphicsPath(PointF[] points, byte[] types, XFillMode fillMode) + { +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath = new GraphicsPath(points, types, (FillMode)fillMode); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF // Is true only in Hybrid build. + _pathGeometry = new PathGeometry(); + _pathGeometry.FillRule = FillRule.EvenOdd; +#endif + } +#endif + +#if WPF || NETFX_CORE + /// + /// Gets the current path figure. + /// + PathFigure CurrentPathFigure + { + get + { + int count = _pathGeometry.Figures.Count; + if (count == 0) + { + // Create new figure if there is none. + _pathGeometry.Figures.Add(new PathFigure()); + count++; + } + else + { + PathFigure lastFigure = _pathGeometry.Figures[count - 1]; + if (lastFigure.IsClosed) + { + if (lastFigure.Segments.Count > 0) + { + // Create new figure if previous one was closed. + _pathGeometry.Figures.Add(new PathFigure()); + count++; + } + else + { + Debug.Assert(false); + } + } + } + // Return last figure in collection. + return _pathGeometry.Figures[count - 1]; + } + } + + /// + /// Gets the current path figure, but never created a new one. + /// + PathFigure PeekCurrentFigure + { + get + { + int count = _pathGeometry.Figures.Count; + return count == 0 ? null : _pathGeometry.Figures[count - 1]; + } + } +#endif + + /// + /// Clones this instance. + /// + public XGraphicsPath Clone() + { + XGraphicsPath path = (XGraphicsPath)MemberwiseClone(); +#if CORE + _corePath = new CoreGraphicsPath(_corePath); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + path._gdipPath = _gdipPath.Clone() as GraphicsPath; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + path._pathGeometry = _pathGeometry.Clone(); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight cannot clone geometry objects."); + // TODO: make it manually... +#pragma warning disable 0162 +#endif +#endif + return path; + } + + // ----- AddLine ------------------------------------------------------------------------------ + +#if GDI + /// + /// Adds a line segment to current figure. + /// + public void AddLine(System.Drawing.Point pt1, System.Drawing.Point pt2) + { + AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + } +#endif + +#if WPF + /// + /// Adds a line segment to current figure. + /// + public void AddLine(SysPoint pt1, SysPoint pt2) + { + AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + } +#endif + +#if GDI + /// + /// Adds a line segment to current figure. + /// + public void AddLine(PointF pt1, PointF pt2) + { + AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + } +#endif + + /// + /// Adds a line segment to current figure. + /// + public void AddLine(XPoint pt1, XPoint pt2) + { + AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Adds a line segment to current figure. + /// + public void AddLine(double x1, double y1, double x2, double y2) + { +#if CORE + _corePath.MoveOrLineTo(x1, y1); + _corePath.LineTo(x2, y2, false); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddLine((float)x1, (float)y1, (float)x2, (float)y2); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + { + figure.StartPoint = new SysPoint(x1, y1); +#if !SILVERLIGHT + var lineSegment = new LineSegment(new SysPoint(x2, y2), true); +#else + var lineSegment = new LineSegment { Point = new Point(x2, y2) }; +#endif + figure.Segments.Add(lineSegment); + } + else + { +#if !SILVERLIGHT + var lineSegment1 = new LineSegment(new SysPoint(x1, y1), true); + var lineSegment2 = new LineSegment(new SysPoint(x2, y2), true); +#else + var lineSegment1 = new LineSegment { Point = new Point(x1, y1) }; + var lineSegment2 = new LineSegment { Point = new Point(x2, y2) }; +#endif + figure.Segments.Add(lineSegment1); + figure.Segments.Add(lineSegment2); + } +#endif + } + + // ----- AddLines ----------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a series of connected line segments to current figure. + /// + public void AddLines(System.Drawing.Point[] points) + { + AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if WPF + /// + /// Adds a series of connected line segments to current figure. + /// + public void AddLines(SysPoint[] points) + { + AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Adds a series of connected line segments to current figure. + /// + public void AddLines(PointF[] points) + { + AddLines(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Adds a series of connected line segments to current figure. + /// + public void AddLines(XPoint[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count == 0) + return; +#if CORE + _corePath.MoveOrLineTo(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx++) + _corePath.LineTo(points[idx].X, points[idx].Y, false); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddLines(XGraphics.MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + { + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx++) + { +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(new SysPoint(points[idx].X, points[idx].Y), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = new Point(points[idx].X, points[idx].Y); // ,true? +#endif + figure.Segments.Add(lineSegment); + } + } + else + { + for (int idx = 0; idx < count; idx++) + { + // figure.Segments.Add(new LineSegment(new SysPoint(points[idx].x, points[idx].y), true)); +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(new SysPoint(points[idx].X, points[idx].Y), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = new Point(points[idx].X, points[idx].Y); // ,true? +#endif + figure.Segments.Add(lineSegment); + } + } +#endif + } + + // ----- AddBezier ---------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a cubic Bzier curve to the current figure. + /// + public void AddBezier(System.Drawing.Point pt1, System.Drawing.Point pt2, System.Drawing.Point pt3, System.Drawing.Point pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } +#endif + +#if WPF + /// + /// Adds a cubic Bzier curve to the current figure. + /// + public void AddBezier(SysPoint pt1, SysPoint pt2, SysPoint pt3, SysPoint pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } +#endif + +#if GDI + /// + /// Adds a cubic Bzier curve to the current figure. + /// + public void AddBezier(PointF pt1, PointF pt2, PointF pt3, PointF pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } +#endif + + /// + /// Adds a cubic Bzier curve to the current figure. + /// + public void AddBezier(XPoint pt1, XPoint pt2, XPoint pt3, XPoint pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Adds a cubic Bzier curve to the current figure. + /// + public void AddBezier(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { +#if CORE + _corePath.MoveOrLineTo(x1, y1); + _corePath.BezierTo(x2, y2, x3, y3, x4, y4, false); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddBezier((float)x1, (float)y1, (float)x2, (float)y2, (float)x3, (float)y3, (float)x4, (float)y4); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + figure.StartPoint = new SysPoint(x1, y1); + else + { + // figure.Segments.Add(new LineSegment(new SysPoint(x1, y1), true)); +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(new SysPoint(x1, y1), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = new Point(x1, y1); +#endif + figure.Segments.Add(lineSegment); + } + //figure.Segments.Add(new BezierSegment( + // new SysPoint(x2, y2), + // new SysPoint(x3, y3), + // new SysPoint(x4, y4), true)); +#if !SILVERLIGHT + BezierSegment bezierSegment = new BezierSegment( + new SysPoint(x2, y2), + new SysPoint(x3, y3), + new SysPoint(x4, y4), true); +#else + BezierSegment bezierSegment = new BezierSegment(); + bezierSegment.Point1 = new Point(x2, y2); + bezierSegment.Point2 = new Point(x3, y3); + bezierSegment.Point3 = new Point(x4, y4); +#endif + figure.Segments.Add(bezierSegment); +#endif + } + + // ----- AddBeziers --------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a sequence of connected cubic Bzier curves to the current figure. + /// + public void AddBeziers(System.Drawing.Point[] points) + { + AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if WPF + /// + /// Adds a sequence of connected cubic Bzier curves to the current figure. + /// + public void AddBeziers(SysPoint[] points) + { + AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Adds a sequence of connected cubic Bzier curves to the current figure. + /// + public void AddBeziers(PointF[] points) + { + AddBeziers(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Adds a sequence of connected cubic Bzier curves to the current figure. + /// + public void AddBeziers(XPoint[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + int count = points.Length; + if (count < 4) + throw new ArgumentException("At least four points required for bezier curve.", "points"); + + if ((count - 1) % 3 != 0) + throw new ArgumentException("Invalid number of points for bezier curve. Number must fulfil 4+3n.", + "points"); + +#if CORE + _corePath.MoveOrLineTo(points[0].X, points[0].Y); + for (int idx = 1; idx < count; idx += 3) + { + _corePath.BezierTo(points[idx].X, points[idx].Y, points[idx + 1].X, points[idx + 1].Y, + points[idx + 2].X, points[idx + 2].Y, false); + } +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddBeziers(XGraphics.MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + else + { + // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(new SysPoint(points[0].X, points[0].Y), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = new Point(points[0].X, points[0].Y); +#endif + figure.Segments.Add(lineSegment); + } + for (int idx = 1; idx < count; idx += 3) + { + //figure.Segments.Add(new BezierSegment( + // new SysPoint(points[idx].x, points[idx].y), + // new SysPoint(points[idx + 1].x, points[idx + 1].y), + // new SysPoint(points[idx + 2].x, points[idx + 2].y), true)); +#if !SILVERLIGHT + BezierSegment bezierSegment = new BezierSegment( + new SysPoint(points[idx].X, points[idx].Y), + new SysPoint(points[idx + 1].X, points[idx + 1].Y), + new SysPoint(points[idx + 2].X, points[idx + 2].Y), true); +#else + BezierSegment bezierSegment = new BezierSegment(); + bezierSegment.Point1 = new Point(points[idx].X, points[idx].Y); + bezierSegment.Point2 = new Point(points[idx + 1].X, points[idx + 1].Y); + bezierSegment.Point3 = new Point(points[idx + 2].X, points[idx + 2].Y); +#endif + figure.Segments.Add(bezierSegment); + } +#endif + } + + // ----- AddCurve ----------------------------------------------------------------------- + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(System.Drawing.Point[] points) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if WPF + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(SysPoint[] points) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(PointF[] points) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length)); + } +#endif + + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(XPoint[] points) + { + AddCurve(points, 0.5); + } + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(System.Drawing.Point[] points, double tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + +#if WPF + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(SysPoint[] points, double tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(PointF[] points, double tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(XPoint[] points, double tension) + { + int count = points.Length; + if (count < 2) + throw new ArgumentException("AddCurve requires two or more points.", "points"); +#if CORE + _corePath.AddCurve(points, tension); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddCurve(XGraphics.MakePointFArray(points), (float)tension); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + tension /= 3; + + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + else + { + // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(new SysPoint(points[0].X, points[0].Y), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = new Point(points[0].X, points[0].Y); +#endif + figure.Segments.Add(lineSegment); + } + + if (count == 2) + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); + } + else + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[2], tension)); + for (int idx = 1; idx < count - 2; idx++) + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[count - 1], tension)); + } +#endif + } + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(System.Drawing.Point[] points, int offset, int numberOfSegments, float tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); + } +#endif + +#if WPF + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(SysPoint[] points, int offset, int numberOfSegments, float tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); + } +#endif + +#if GDI + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(PointF[] points, int offset, int numberOfSegments, float tension) + { + AddCurve(XGraphics.MakeXPointArray(points, 0, points.Length), offset, numberOfSegments, tension); + } +#endif + + /// + /// Adds a spline curve to the current figure. + /// + public void AddCurve(XPoint[] points, int offset, int numberOfSegments, double tension) + { +#if CORE + throw new NotImplementedException("AddCurve not yet implemented."); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddCurve(XGraphics.MakePointFArray(points), offset, numberOfSegments, (float)tension); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + throw new NotImplementedException("AddCurve not yet implemented."); +#endif + } + + // ----- AddArc ------------------------------------------------------------------------------- + +#if GDI + /// + /// Adds an elliptical arc to the current figure. + /// + public void AddArc(Rectangle rect, double startAngle, double sweepAngle) + { + AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + +#if GDI + /// + /// Adds an elliptical arc to the current figure. + /// + public void AddArc(RectangleF rect, double startAngle, double sweepAngle) + { + AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Adds an elliptical arc to the current figure. + /// + public void AddArc(XRect rect, double startAngle, double sweepAngle) + { + AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Adds an elliptical arc to the current figure. + /// + public void AddArc(double x, double y, double width, double height, double startAngle, double sweepAngle) + { +#if CORE + _corePath.AddArc(x, y, width, height, startAngle, sweepAngle); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddArc((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + SysPoint startPoint; + ArcSegment seg = GeometryHelper.CreateArcSegment(x, y, width, height, startAngle, sweepAngle, out startPoint); + if (figure.Segments.Count == 0) + figure.StartPoint = startPoint; + else + { + //#if !SILVERLIGHT + // LineSegment lineSegment = new LineSegment(startPoint, true); + //#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = startPoint; + //#endif + figure.Segments.Add(lineSegment); + } + + figure.Segments.Add(seg); + + //figure.Segments.Add( + //if (figure.Segments.Count == 0) + // figure.StartPoint = new SysPoint(points[0].x, points[0].y); + //else + // figure.Segments.Add(new LineSegment(new SysPoint(points[0].x, points[0].y), true)); + + //for (int idx = 1; idx < 5555; idx += 3) + // figure.Segments.Add(new BezierSegment( + // new SysPoint(points[idx].x, points[idx].y), + // new SysPoint(points[idx + 1].x, points[idx + 1].y), + // new SysPoint(points[idx + 2].x, points[idx + 2].y), true)); +#endif + } + + /// + /// Adds an elliptical arc to the current figure. The arc is specified WPF like. + /// + public void AddArc(XPoint point1, XPoint point2, XSize size, double rotationAngle, bool isLargeArg, XSweepDirection sweepDirection) + { +#if CORE + _corePath.AddArc(point1, point2, size, rotationAngle, isLargeArg, sweepDirection); +#endif +#if GDI + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddArc"); +#endif +#if WPF + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count == 0) + figure.StartPoint = point1.ToPoint(); + else + { + // figure.Segments.Add(new LineSegment(point1.ToPoint(), true)); +#if !SILVERLIGHT + LineSegment lineSegment = new LineSegment(point1.ToPoint(), true); +#else + LineSegment lineSegment = new LineSegment(); + lineSegment.Point = point1.ToPoint(); +#endif + figure.Segments.Add(lineSegment); + } + + // figure.Segments.Add(new ArcSegment(point2.ToPoint(), size.ToSize(), rotationAngle, isLargeArg, sweepDirection, true)); +#if !SILVERLIGHT + ArcSegment arcSegment = new ArcSegment(point2.ToPoint(), size.ToSize(), rotationAngle, isLargeArg, (SweepDirection)sweepDirection, true); +#else + ArcSegment arcSegment = new ArcSegment(); + arcSegment.Point = point2.ToPoint(); + arcSegment.Size = size.ToSize(); + arcSegment.RotationAngle = rotationAngle; + arcSegment.IsLargeArc = isLargeArg; + arcSegment.SweepDirection = (SweepDirection)sweepDirection; +#endif + figure.Segments.Add(arcSegment); +#endif + } + + // ----- AddRectangle ------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a rectangle to this path. + /// + public void AddRectangle(Rectangle rect) + { + AddRectangle(new XRect(rect)); + } +#endif + +#if GDI + /// + /// Adds a rectangle to this path. + /// + public void AddRectangle(RectangleF rect) + { + AddRectangle(new XRect(rect)); + } +#endif + + /// + /// Adds a rectangle to this path. + /// + public void AddRectangle(XRect rect) + { +#if CORE + _corePath.MoveTo(rect.X, rect.Y); + _corePath.LineTo(rect.X + rect.Width, rect.Y, false); + _corePath.LineTo(rect.X + rect.Width, rect.Y + rect.Height, false); + _corePath.LineTo(rect.X, rect.Y + rect.Height, true); + _corePath.CloseSubpath(); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + // If rect is empty GDI+ removes the rect from the path. + // This is not intended if the path is used for clipping. + // See http://forum.pdfsharp.net/viewtopic.php?p=9433#p9433 + // _gdipPath.AddRectangle(rect.ToRectangleF()); + + // Draw the rectangle manually. + _gdipPath.StartFigure(); + _gdipPath.AddLines(new PointF[] { rect.TopLeft.ToPointF(), rect.TopRight.ToPointF(), rect.BottomRight.ToPointF(), rect.BottomLeft.ToPointF() }); + _gdipPath.CloseFigure(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + StartFigure(); + PathFigure figure = CurrentPathFigure; + figure.StartPoint = new SysPoint(rect.X, rect.Y); + + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y), true)); + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y + rect.height), true)); + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x, rect.y + rect.height), true)); +#if !SILVERLIGHT + LineSegment lineSegment1 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y), true); + LineSegment lineSegment2 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y + rect.Height), true); + LineSegment lineSegment3 = new LineSegment(new SysPoint(rect.X, rect.Y + rect.Height), true); +#else + LineSegment lineSegment1 = new LineSegment(); + lineSegment1.Point = new Point(rect.X + rect.Width, rect.Y); + LineSegment lineSegment2 = new LineSegment(); + lineSegment2.Point = new Point(rect.X + rect.Width, rect.Y + rect.Height); + LineSegment lineSegment3 = new LineSegment(); + lineSegment3.Point = new Point(rect.X, rect.Y + rect.Height); +#endif + figure.Segments.Add(lineSegment1); + figure.Segments.Add(lineSegment2); + figure.Segments.Add(lineSegment3); + CloseFigure(); +#endif + } + + /// + /// Adds a rectangle to this path. + /// + public void AddRectangle(double x, double y, double width, double height) + { + AddRectangle(new XRect(x, y, width, height)); + } + + // ----- AddRectangles ------------------------------------------------------------------------ + +#if GDI + /// + /// Adds a series of rectangles to this path. + /// + public void AddRectangles(Rectangle[] rects) + { + int count = rects.Length; + for (int idx = 0; idx < count; idx++) + AddRectangle(rects[idx]); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddRectangles(rects); + } + finally { Lock.ExitGdiPlus(); } + } +#endif + +#if GDI + /// + /// Adds a series of rectangles to this path. + /// + public void AddRectangles(RectangleF[] rects) + { + int count = rects.Length; + for (int idx = 0; idx < count; idx++) + AddRectangle(rects[idx]); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddRectangles(rects); + } + finally { Lock.ExitGdiPlus(); } + } +#endif + + /// + /// Adds a series of rectangles to this path. + /// + public void AddRectangles(XRect[] rects) + { + int count = rects.Length; + for (int idx = 0; idx < count; idx++) + { +#if CORE + AddRectangle(rects[idx]); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddRectangle(rects[idx].ToRectangleF()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + StartFigure(); + PathFigure figure = CurrentPathFigure; + XRect rect = rects[idx]; + figure.StartPoint = new SysPoint(rect.X, rect.Y); + + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y), true)); + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x + rect.width, rect.y + rect.height), true)); + // figure.Segments.Add(new LineSegment(new SysPoint(rect.x, rect.y + rect.height), true)); +#if !SILVERLIGHT + LineSegment lineSegment1 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y), true); + LineSegment lineSegment2 = new LineSegment(new SysPoint(rect.X + rect.Width, rect.Y + rect.Height), true); + LineSegment lineSegment3 = new LineSegment(new SysPoint(rect.X, rect.Y + rect.Height), true); +#else + LineSegment lineSegment1 = new LineSegment(); + lineSegment1.Point = new Point(rect.X + rect.Width, rect.Y); + LineSegment lineSegment2 = new LineSegment(); + lineSegment2.Point = new Point(rect.X + rect.Width, rect.Y + rect.Height); + LineSegment lineSegment3 = new LineSegment(); + lineSegment3.Point = new Point(rect.X, rect.Y + rect.Height); +#endif + figure.Segments.Add(lineSegment1); + figure.Segments.Add(lineSegment2); + figure.Segments.Add(lineSegment3); + CloseFigure(); +#endif + } + } + + // ----- AddRoundedRectangle ------------------------------------------------------------------ + +#if GDI + /// + /// Adds a rectangle with rounded corners to this path. + /// + public void AddRoundedRectangle(Rectangle rect, System.Drawing.Size ellipseSize) + { + AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Adds a rectangle with rounded corners to this path. + /// + public void AddRoundedRectangle(SysRect rect, SysSize ellipseSize) + { + AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if GDI + /// + /// Adds a rectangle with rounded corners to this path. + /// + public void AddRoundedRectangle(RectangleF rect, SizeF ellipseSize) + { + AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + +#if GDI + /// + /// Adds a rectangle with rounded corners to this path. + /// + public void AddRoundedRectangle(XRect rect, SizeF ellipseSize) + { + AddRoundedRectangle(rect.X, rect.Y, rect.Width, rect.Height, ellipseSize.Width, ellipseSize.Height); + } +#endif + + /// + /// Adds a rectangle with rounded corners to this path. + /// + public void AddRoundedRectangle(double x, double y, double width, double height, double ellipseWidth, double ellipseHeight) + { +#if CORE +#if true + double arcWidth = ellipseWidth / 2; + double arcHeight = ellipseHeight / 2; +#if true // Clockwise + _corePath.MoveTo(x + width - arcWidth, y); + _corePath.QuadrantArcTo(x + width - arcWidth, y + arcHeight, arcWidth, arcHeight, 1, true); + + _corePath.LineTo(x + width, y + height - arcHeight, false); + _corePath.QuadrantArcTo(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight, 4, true); + + _corePath.LineTo(x + arcWidth, y + height, false); + _corePath.QuadrantArcTo(x + arcWidth, y + height - arcHeight, arcWidth, arcHeight, 3, true); + + _corePath.LineTo(x, y + arcHeight, false); + _corePath.QuadrantArcTo(x + arcWidth, y + arcHeight, arcWidth, arcHeight, 2, true); + + _corePath.CloseSubpath(); +#else // Counterclockwise + _corePath.MoveTo(x + arcWidth, y); + _corePath.QuadrantArcTo(x + arcWidth, y + arcHeight, arcWidth, arcHeight, 2, false); + + _corePath.LineTo(x, y + height - arcHeight, false); + _corePath.QuadrantArcTo(x + arcWidth, y + height - arcHeight, arcWidth, arcHeight, 3, false); + + _corePath.LineTo(x + width - arcWidth, y + height, false); + _corePath.QuadrantArcTo(x + width - arcWidth, y + height - arcHeight, arcWidth, arcHeight, 4, false); + + _corePath.LineTo(x + width, y + arcHeight, false); + _corePath.QuadrantArcTo(x + width - arcWidth, y + arcHeight, arcWidth, arcHeight, 1, false); + + _corePath.CloseSubpath(); +#endif +#else + // AddArc not yet implemented + AddArc((float)(x + width - ellipseWidth), (float)y, (float)ellipseWidth, (float)ellipseHeight, -90, 90); + AddArc((float)(x + width - ellipseWidth), (float)(y + height - ellipseHeight), (float)ellipseWidth, + (float)ellipseHeight, 0, 90); + AddArc((float)x, (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 90, 90); + AddArc((float)x, (float)y, (float)ellipseWidth, (float)ellipseHeight, 180, 90); + CloseFigure(); +#endif +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.StartFigure(); + _gdipPath.AddArc((float)(x + width - ellipseWidth), (float)y, (float)ellipseWidth, (float)ellipseHeight, -90, 90); + _gdipPath.AddArc((float)(x + width - ellipseWidth), (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 0, 90); + _gdipPath.AddArc((float)x, (float)(y + height - ellipseHeight), (float)ellipseWidth, (float)ellipseHeight, 90, 90); + _gdipPath.AddArc((float)x, (float)y, (float)ellipseWidth, (float)ellipseHeight, 180, 90); + _gdipPath.CloseFigure(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + double ex = ellipseWidth / 2; + double ey = ellipseHeight / 2; + StartFigure(); + PathFigure figure = CurrentPathFigure; + figure.StartPoint = new SysPoint(x + ex, y); + + //#if !SILVERLIGHT + // figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y), true)); + // // TODOWPF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx + // figure.Segments.Add(new ArcSegment(new SysPoint(x + width, y + ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + // //figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + ey), true)); + + // figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + height - ey), true)); + // // TODOWPF + // figure.Segments.Add(new ArcSegment(new SysPoint(x + width - ex, y + height), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + // //figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y + height), true)); + + // figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y + height), true)); + // // TODOWPF + // figure.Segments.Add(new ArcSegment(new SysPoint(x, y + height - ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + // //figure.Segments.Add(new LineSegment(new SysPoint(x, y + height - ey), true)); + + // figure.Segments.Add(new LineSegment(new SysPoint(x, y + ey), true)); + // // TODOWPF + // figure.Segments.Add(new ArcSegment(new SysPoint(x + ex, y), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + // //figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y), true)); + //#else + +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y), true)); +#else + figure.Segments.Add(new LineSegment { Point = new SysPoint(x + width - ex, y) }); +#endif + + // TODOWPF XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new ArcSegment(new SysPoint(x + width, y + ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + //figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + ey), true)); +#else + figure.Segments.Add(new ArcSegment + { + Point = new SysPoint(x + width, y + ey), + Size = new SysSize(ex, ey), + //RotationAngle = 0, + //IsLargeArc = false, + SweepDirection = SweepDirection.Clockwise + }); +#endif + +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new LineSegment(new SysPoint(x + width, y + height - ey), true)); +#else + figure.Segments.Add(new LineSegment { Point = new SysPoint(x + width, y + height - ey) }); +#endif + + // TODOWPF +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new ArcSegment(new SysPoint(x + width - ex, y + height), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + //figure.Segments.Add(new LineSegment(new SysPoint(x + width - ex, y + height), true)); +#else + figure.Segments.Add(new ArcSegment + { + Point = new SysPoint(x + width - ex, y + height), + Size = new SysSize(ex, ey), + //RotationAngle = 0, + //IsLargeArc = false, + SweepDirection = SweepDirection.Clockwise + }); +#endif + +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y + height), true)); +#else + figure.Segments.Add(new LineSegment { Point = new SysPoint(x + ex, y + height) }); +#endif + + // TODOWPF +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new ArcSegment(new SysPoint(x, y + height - ey), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + //figure.Segments.Add(new LineSegment(new SysPoint(x, y + height - ey), true)); +#else + figure.Segments.Add(new ArcSegment + { + Point = new SysPoint(x, y + height - ey), + Size = new SysSize(ex, ey), + //RotationAngle = 0, + //IsLargeArc = false, + SweepDirection = SweepDirection.Clockwise + }); +#endif + +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new LineSegment(new SysPoint(x, y + ey), true)); +#else + figure.Segments.Add(new LineSegment { Point = new SysPoint(x, y + ey) }); +#endif + + // TODOWPF +#if !SILVERLIGHT && !NETFX_CORE + figure.Segments.Add(new ArcSegment(new SysPoint(x + ex, y), new SysSize(ex, ey), 0, false, SweepDirection.Clockwise, true)); + //figure.Segments.Add(new LineSegment(new SysPoint(x + ex, y), true)); +#else + figure.Segments.Add(new ArcSegment + { + Point = new SysPoint(x + ex, y), + Size = new SysSize(ex, ey), + //RotationAngle = 0, + //IsLargeArc = false, + SweepDirection = SweepDirection.Clockwise + }); +#endif + CloseFigure(); +#endif + } + + // ----- AddEllipse --------------------------------------------------------------------------- + +#if GDI + /// + /// Adds an ellipse to the current path. + /// + public void AddEllipse(Rectangle rect) + { + AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + +#if GDI + /// + /// Adds an ellipse to the current path. + /// + public void AddEllipse(RectangleF rect) + { + AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + /// + /// Adds an ellipse to the current path. + /// + public void AddEllipse(XRect rect) + { + AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Adds an ellipse to the current path. + /// + public void AddEllipse(double x, double y, double width, double height) + { +#if CORE + double w = width / 2; + double h = height / 2; + double xc = x + w; + double yc = y + h; + _corePath.MoveTo(x + w, y); + _corePath.QuadrantArcTo(xc, yc, w, h, 1, true); + _corePath.QuadrantArcTo(xc, yc, w, h, 4, true); + _corePath.QuadrantArcTo(xc, yc, w, h, 3, true); + _corePath.QuadrantArcTo(xc, yc, w, h, 2, true); + _corePath.CloseSubpath(); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddEllipse((float)x, (float)y, (float)width, (float)height); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry.AddGeometry(new EllipseGeometry(new Rect(x, y, width, height))); +#else + var figure = new PathFigure(); + figure.StartPoint = new SysPoint(x, y + height / 2); + var segment = new ArcSegment + { + Point = new SysPoint(x + width, y + height / 2), + Size = new SysSize(width / 2, height / 2), + IsLargeArc = true, + RotationAngle = 180, + SweepDirection = SweepDirection.Clockwise, + }; + figure.Segments.Add(segment); + segment = new ArcSegment + { + Point = figure.StartPoint, + Size = new SysSize(width / 2, height / 2), + IsLargeArc = true, + RotationAngle = 180, + SweepDirection = SweepDirection.Clockwise, + }; + figure.Segments.Add(segment); + _pathGeometry.Figures.Add(figure); +#endif + // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, + // but CloseFigure() is needed for the next adding not to continue this figure. + CloseFigure(); +#endif + } + + // ----- AddPolygon --------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a polygon to this path. + /// + public void AddPolygon(System.Drawing.Point[] points) + { + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPolygon(points); + } + finally { Lock.ExitGdiPlus(); } + } +#endif + +#if WPF || NETFX_CORE + /// + /// Adds a polygon to this path. + /// + public void AddPolygon(SysPoint[] points) + { + // TODO: fill mode unclear here +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry.AddGeometry(GeometryHelper.CreatePolygonGeometry(points, XFillMode.Alternate, true)); + CloseFigure(); // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, but CloseFigure() is needed for the next adding not to continue this figure. +#else + AddPolygon(XGraphics.MakeXPointArray(points, 0, points.Length)); +#endif + } +#endif + +#if GDI + /// + /// Adds a polygon to this path. + /// + public void AddPolygon(PointF[] points) + { + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPolygon(points); + } + finally { Lock.ExitGdiPlus(); } + } +#endif + + /// + /// Adds a polygon to this path. + /// + public void AddPolygon(XPoint[] points) + { +#if CORE + int count = points.Length; + if (count == 0) + return; + + _corePath.MoveTo(points[0].X, points[0].Y); + for (int idx = 0; idx < count - 1; idx++) + _corePath.LineTo(points[idx].X, points[idx].Y, false); + _corePath.LineTo(points[count - 1].X, points[count - 1].Y, true); + _corePath.CloseSubpath(); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPolygon(XGraphics.MakePointFArray(points)); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry.AddGeometry(GeometryHelper.CreatePolygonGeometry(XGraphics.MakePointArray(points), XFillMode.Alternate, true)); +#else + var figure = new PathFigure(); + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + figure.IsClosed = true; + + PolyLineSegment segment = new PolyLineSegment(); + int count = points.Length; + // For correct drawing the start point of the segment must not be the same as the first point. + for (int idx = 1; idx < count; idx++) + segment.Points.Add(new SysPoint(points[idx].X, points[idx].Y)); +#if !SILVERLIGHT && !NETFX_CORE + seg.IsStroked = true; +#endif + figure.Segments.Add(segment); + _pathGeometry.Figures.Add(figure); +#endif + // TODO: NOT NEEDED + //CloseFigure(); // StartFigure() isn't needed because AddGeometry() implicitly starts a new figure, but CloseFigure() is needed for the next adding not to continue this figure. +#endif + } + + // ----- AddPie ------------------------------------------------------------------------------- + +#if GDI + /// + /// Adds the outline of a pie shape to this path. + /// + public void AddPie(Rectangle rect, double startAngle, double sweepAngle) + { + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPie(rect, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } + } +#endif + +#if GDI + /// + /// Adds the outline of a pie shape to this path. + /// + public void AddPie(RectangleF rect, double startAngle, double sweepAngle) + { + AddPie(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } +#endif + + /// + /// Adds the outline of a pie shape to this path. + /// + public void AddPie(XRect rect, double startAngle, double sweepAngle) + { + AddPie(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Adds the outline of a pie shape to this path. + /// + public void AddPie(double x, double y, double width, double height, double startAngle, double sweepAngle) + { +#if CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPie"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPie((float)x, (float)y, (float)width, (float)height, (float)startAngle, (float)sweepAngle); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPie"); +#endif + } + + // ----- AddClosedCurve ------------------------------------------------------------------------ + +#if GDI + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(System.Drawing.Point[] points) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(SysPoint[] points) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); + } +#endif + +#if GDI + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(PointF[] points) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), 0.5); + } +#endif + + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(XPoint[] points) + { + AddClosedCurve(points, 0.5); + } + +#if GDI + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(System.Drawing.Point[] points, double tension) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(SysPoint[] points, double tension) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + +#if GDI + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(PointF[] points, double tension) + { + AddClosedCurve(XGraphics.MakeXPointArray(points, 0, points.Length), tension); + } +#endif + + /// + /// Adds a closed curve to this path. + /// + public void AddClosedCurve(XPoint[] points, double tension) + { + if (points == null) + throw new ArgumentNullException("points"); + int count = points.Length; + if (count == 0) + return; + if (count < 2) + throw new ArgumentException("Not enough points.", "points"); + +#if CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddClosedCurve"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddClosedCurve(XGraphics.MakePointFArray(points), (float)tension); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF ||NETFX_CORE + tension /= 3; + + StartFigure(); + PathFigure figure = CurrentPathFigure; + figure.StartPoint = new SysPoint(points[0].X, points[0].Y); + + if (count == 2) + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[0], points[0], points[1], points[1], tension)); + } + else + { + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 1], points[0], points[1], points[2], tension)); + for (int idx = 1; idx < count - 2; idx++) + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[idx - 1], points[idx], points[idx + 1], points[idx + 2], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 3], points[count - 2], points[count - 1], points[0], tension)); + figure.Segments.Add(GeometryHelper.CreateCurveSegment(points[count - 2], points[count - 1], points[0], points[1], tension)); + } +#endif + } + + // ----- AddPath ------------------------------------------------------------------------------ + + /// + /// Adds the specified path to this path. + /// + public void AddPath(XGraphicsPath path, bool connect) + { +#if CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddPath"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddPath(path._gdipPath, connect); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry.AddGeometry(path._pathGeometry); +#else + // AG-HACK: No AddGeometry in Silverlight version of PathGeometry + throw new InvalidOperationException("Silverlight/WinRT cannot merge geometry objects."); + // TODO: make it manually by using a GeometryGroup +#endif +#endif + } + + // ----- AddString ---------------------------------------------------------------------------- + +#if GDI + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, System.Drawing.Point origin, XStringFormat format) + { + AddString(s, family, style, emSize, new XRect(origin.X, origin.Y, 0, 0), format); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, SysPoint origin, XStringFormat format) + { + AddString(s, family, style, emSize, new XPoint(origin), format); + } +#endif + +#if GDI + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, PointF origin, XStringFormat format) + { + AddString(s, family, style, emSize, new XRect(origin.X, origin.Y, 0, 0), format); + } +#endif + + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, XPoint origin, + XStringFormat format) + { + try + { +#if CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddString"); +#endif +#if GDI + if (family.GdiFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); + + PointF p = origin.ToPointF(); + p.Y += SimulateBaselineOffset(family, style, emSize, format); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, p, format.RealizeGdiStringFormat()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF + if (family.WpfFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); +#if !SILVERLIGHT + XFont font = new XFont(family.Name, emSize, style); + + double x = origin.X; + double y = origin.Y; + + double lineSpace = font.GetHeight(); + double cyAscent = lineSpace * font.CellAscent / font.CellSpace; + double cyDescent = lineSpace * font.CellDescent / font.CellSpace; + + Typeface typeface = FontHelper.CreateTypeface(family.WpfFamily, style); + FormattedText formattedText = FontHelper.CreateFormattedText(s, typeface, emSize, WpfBrushes.Black); + + switch (format.Alignment) + { + case XStringAlignment.Near: + // nothing to do, this is the default + //formattedText.TextAlignment = TextAlignment.Left; + break; + + case XStringAlignment.Center: + formattedText.TextAlignment = TextAlignment.Center; + break; + + case XStringAlignment.Far: + formattedText.TextAlignment = TextAlignment.Right; + break; + } + switch (format.LineAlignment) + { + case XLineAlignment.Near: + //y += cyAscent; + break; + + case XLineAlignment.Center: + // TODO use CapHeight. PDFlib also uses 3/4 of ascent + y += -lineSpace / 2; //-formattedText.Baseline + (cyAscent * 2 / 4); + break; + + case XLineAlignment.Far: + y += -formattedText.Baseline - cyDescent; + break; + + case XLineAlignment.BaseLine: + y -= formattedText.Baseline; + break; + } + + Geometry geo = formattedText.BuildGeometry(new XPoint(x, y)); + _pathGeometry.AddGeometry(geo); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight cannot create geometry of glyphs."); + // TODO: Get the outline directly from the font. +#endif +#endif + } + catch + { + throw; + } + } + +#if GDI + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, Rectangle layoutRect, XStringFormat format) + { + if (family.GdiFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); + + Rectangle rect = new Rectangle(layoutRect.X, layoutRect.Y, layoutRect.Width, layoutRect.Height); + rect.Offset(new System.Drawing.Point(0, (int)SimulateBaselineOffset(family, style, emSize, format))); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, rect, format.RealizeGdiStringFormat()); + } + finally { Lock.ExitGdiPlus(); } + } + + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, RectangleF layoutRect, XStringFormat format) + { + if (family.GdiFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); + + RectangleF rect = new RectangleF(layoutRect.X, layoutRect.Y, layoutRect.Width, layoutRect.Height); + rect.Offset(new PointF(0, SimulateBaselineOffset(family, style, emSize, format))); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, layoutRect, format.RealizeGdiStringFormat()); + } + finally { Lock.ExitGdiPlus(); } + } + + /// + /// Calculates the offset for BaseLine positioning simulation: + /// In GDI we have only Near, Center and Far as LineAlignment and no BaseLine. For XLineAlignment.BaseLine StringAlignment.Near is returned. + /// We now return the negative drawed ascender height. + /// This has to be added to the LayoutRect/Origin before each _gdipPath.AddString(). + /// + /// + /// + /// + /// + /// + private float SimulateBaselineOffset(XFontFamily family, XFontStyle style, double emSize, XStringFormat format) + { + XFont font = new XFont(family.Name, emSize, style); + + if (format.LineAlignment == XLineAlignment.BaseLine) + { + double lineSpace = font.GetHeight(); + int cellSpace = font.FontFamily.GetLineSpacing(font.Style); + int cellAscent = font.FontFamily.GetCellAscent(font.Style); + int cellDescent = font.FontFamily.GetCellDescent(font.Style); + double cyAscent = lineSpace * cellAscent / cellSpace; + cyAscent = lineSpace * font.CellAscent / font.CellSpace; + return (float)-cyAscent; + } + return 0; + } + +#endif + +#if WPF + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, Rect rect, XStringFormat format) + { + //gdip Path.AddString(s, family.gdiFamily, (int)style, (float)emSize, layoutRect, format.RealizeGdiStringFormat()); + AddString(s, family, style, emSize, new XRect(rect), format); + } +#endif + + /// + /// Adds a text string to this path. + /// + public void AddString(string s, XFontFamily family, XFontStyle style, double emSize, XRect layoutRect, + XStringFormat format) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (family == null) + throw new ArgumentNullException("family"); + + if (format == null) + format = XStringFormats.Default; + + if (format.LineAlignment == XLineAlignment.BaseLine && layoutRect.Height != 0) + throw new InvalidOperationException( + "DrawString: With XLineAlignment.BaseLine the height of the layout rectangle must be 0."); + + if (s.Length == 0) + return; + + XFont font = new XFont(family.Name, emSize, style); +#if CORE + DiagnosticsHelper.HandleNotImplemented("XGraphicsPath.AddString"); +#endif +#if (GDI || CORE_) && !WPF + //Gfx.DrawString(text, font.Realize_GdiFont(), brush.RealizeGdiBrush(), rect, + // format != null ? format.RealizeGdiStringFormat() : null); + + if (family.GdiFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); + + RectangleF rect = layoutRect.ToRectangleF(); + rect.Offset(new PointF(0, SimulateBaselineOffset(family, style, emSize, format))); + + try + { + Lock.EnterGdiPlus(); + _gdipPath.AddString(s, family.GdiFamily, (int)style, (float)emSize, rect, format.RealizeGdiStringFormat()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF && !GDI + if (family.WpfFamily == null) + throw new NotFiniteNumberException(PSSR.NotImplementedForFontsRetrievedWithFontResolver(family.Name)); +#if !SILVERLIGHT + // Just a first sketch, but currently we do not need it and there is enough to do... + double x = layoutRect.X; + double y = layoutRect.Y; + + //double lineSpace = font.GetHeight(this); + //double cyAscent = lineSpace * font.cellAscent / font.cellSpace; + //double cyDescent = lineSpace * font.cellDescent / font.cellSpace; + + //double cyAscent = family.GetCellAscent(style) * family.GetLineSpacing(style) / family.getl; //fontlineSpace * font.cellAscent / font.cellSpace; + //double cyDescent =family.GetCellDescent(style); // lineSpace * font.cellDescent / font.cellSpace; + double lineSpace = font.GetHeight(); + double cyAscent = lineSpace * font.CellAscent / font.CellSpace; + double cyDescent = lineSpace * font.CellDescent / font.CellSpace; + + bool bold = (style & XFontStyle.Bold) != 0; + bool italic = (style & XFontStyle.Italic) != 0; + bool strikeout = (style & XFontStyle.Strikeout) != 0; + bool underline = (style & XFontStyle.Underline) != 0; + + Typeface typeface = FontHelper.CreateTypeface(family.WpfFamily, style); + //FormattedText formattedText = new FormattedText(s, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, emSize, WpfBrushes.Black); + FormattedText formattedText = FontHelper.CreateFormattedText(s, typeface, emSize, WpfBrushes.Black); + + switch (format.Alignment) + { + case XStringAlignment.Near: + // nothing to do, this is the default + //formattedText.TextAlignment = TextAlignment.Left; + break; + + case XStringAlignment.Center: + x += layoutRect.Width / 2; + formattedText.TextAlignment = TextAlignment.Center; + break; + + case XStringAlignment.Far: + x += layoutRect.Width; + formattedText.TextAlignment = TextAlignment.Right; + break; + } + //if (PageDirection == XPageDirection.Downwards) + //{ + switch (format.LineAlignment) + { + case XLineAlignment.Near: + //y += cyAscent; + break; + + case XLineAlignment.Center: + // TO/DO use CapHeight. PDFlib also uses 3/4 of ascent + //y += -formattedText.Baseline + (cyAscent * 2 / 4) + layoutRect.Height / 2; + + // GDI seems to make it this simple: + // TODO: Check WPF's vertical alignment and make all implementations fit. $MaOs + y += layoutRect.Height / 2 - lineSpace / 2; + break; + + case XLineAlignment.Far: + y += -formattedText.Baseline - cyDescent + layoutRect.Height; + break; + + case XLineAlignment.BaseLine: + y -= formattedText.Baseline; + break; + } + //} + //else + //{ + // // TODOWPF + // switch (format.LineAlignment) + // { + // case XLineAlignment.Near: + // //y += cyDescent; + // break; + + // case XLineAlignment.Center: + // // TODO use CapHeight. PDFlib also uses 3/4 of ascent + // //y += -(cyAscent * 3 / 4) / 2 + rect.Height / 2; + // break; + + // case XLineAlignment.Far: + // //y += -cyAscent + rect.Height; + // break; + + // case XLineAlignment.BaseLine: + // // nothing to do + // break; + // } + //} + + //if (bold && !descriptor.IsBoldFace) + //{ + // // TODO: emulate bold by thicker outline + //} + + //if (italic && !descriptor.IsItalicFace) + //{ + // // TODO: emulate italic by shearing transformation + //} + + if (underline) + { + //double underlinePosition = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlinePosition / font.cellSpace; + //double underlineThickness = lineSpace * realizedFont.FontDescriptor.descriptor.UnderlineThickness / font.cellSpace; + //DrawRectangle(null, brush, x, y - underlinePosition, width, underlineThickness); + } + + if (strikeout) + { + //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace; + //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace; + //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); + } + + //dc.DrawText(formattedText, layoutRectangle.Location.ToPoint()); + //dc.DrawText(formattedText, new SysPoint(x, y)); + + Geometry geo = formattedText.BuildGeometry(new Point(x, y)); + _pathGeometry.AddGeometry(geo); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight cannot create geometry of glyphs."); + // TODO: no, yagni +#endif +#endif + } + + // -------------------------------------------------------------------------------------------- + + /// + /// Closes the current figure and starts a new figure. + /// + public void CloseFigure() + { +#if CORE + _corePath.CloseSubpath(); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.CloseFigure(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + PathFigure figure = PeekCurrentFigure; + if (figure != null && figure.Segments.Count != 0) + figure.IsClosed = true; +#endif + } + + /// + /// Starts a new figure without closing the current figure. + /// + public void StartFigure() + { +#if CORE + // TODO: ??? +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.StartFigure(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + PathFigure figure = CurrentPathFigure; + if (figure.Segments.Count != 0) + { + figure = new PathFigure(); + _pathGeometry.Figures.Add(figure); + } +#endif + } + + // -------------------------------------------------------------------------------------------- + + /// + /// Gets or sets an XFillMode that determines how the interiors of shapes are filled. + /// + public XFillMode FillMode + { + get { return _fillMode; } + set + { + _fillMode = value; +#if CORE + // Nothing to do. +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.FillMode = (FillMode)value; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE + _pathGeometry.FillRule = value == XFillMode.Winding ? FillRule.Nonzero : FillRule.EvenOdd; +#endif + } + } + + private XFillMode _fillMode; + + // -------------------------------------------------------------------------------------------- + + /// + /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. + /// + public void Flatten() + { +#if CORE + // Just do nothing. +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Flatten(); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); +#else + // AGHACK + throw new InvalidOperationException("Silverlight/WinrRT cannot flatten a geometry."); + // TODO: no, yagni +#endif +#endif + } + + /// + /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. + /// + public void Flatten(XMatrix matrix) + { +#if CORE + // Just do nothing. +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Flatten(matrix.ToGdiMatrix()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); + _pathGeometry.Transform = new MatrixTransform(matrix.ToWpfMatrix()); +#else + // AGHACK + throw new InvalidOperationException("Silverlight/WinRT cannot flatten a geometry."); + // TODO: no, yagni +#endif +#endif + } + + /// + /// Converts each curve in this XGraphicsPath into a sequence of connected line segments. + /// + public void Flatten(XMatrix matrix, double flatness) + { +#if CORE + // Just do nothing. +#endif +#if CORE___ + throw new NotImplementedException("XGraphicsPath.Flatten"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Flatten(matrix.ToGdiMatrix(), (float)flatness); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetFlattenedPathGeometry(); + // TODO: matrix handling not yet tested + if (!matrix.IsIdentity) + _pathGeometry.Transform = new MatrixTransform(matrix.ToWpfMatrix()); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight/WinRT cannot flatten a geometry."); + // TODO: no, yagni +#endif +#endif + } + + // -------------------------------------------------------------------------------------------- + + /// + /// Replaces this path with curves that enclose the area that is filled when this path is drawn + /// by the specified pen. + /// + public void Widen(XPen pen) + { +#if CORE + // Just do nothing. +#endif +#if CORE___ + throw new NotImplementedException("XGraphicsPath.Widen"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Widen(pen.RealizeGdiPen()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); + // TODO: no, yagni +#endif +#endif + } + + /// + /// Replaces this path with curves that enclose the area that is filled when this path is drawn + /// by the specified pen. + /// + public void Widen(XPen pen, XMatrix matrix) + { +#if CORE + // Just do nothing. +#endif +#if CORE + throw new NotImplementedException("XGraphicsPath.Widen"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Widen(pen.RealizeGdiPen(), matrix.ToGdiMatrix()); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); + // TODO: no, yagni +#endif +#endif + } + + /// + /// Replaces this path with curves that enclose the area that is filled when this path is drawn + /// by the specified pen. + /// + public void Widen(XPen pen, XMatrix matrix, double flatness) + { +#if CORE + // Just do nothing. +#endif +#if CORE__ + throw new NotImplementedException("XGraphicsPath.Widen"); +#endif +#if GDI + try + { + Lock.EnterGdiPlus(); + _gdipPath.Widen(pen.RealizeGdiPen(), matrix.ToGdiMatrix(), (float)flatness); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF || NETFX_CORE +#if !SILVERLIGHT && !NETFX_CORE + _pathGeometry = _pathGeometry.GetWidenedPathGeometry(pen.RealizeWpfPen()); +#else + // AG-HACK + throw new InvalidOperationException("Silverlight/WinRT cannot widen a geometry."); + // TODO: no, yagni +#endif +#endif + } + + /// + /// Grants access to internal objects of this class. + /// + public XGraphicsPathInternals Internals + { + get { return new XGraphicsPathInternals(this); } + } + +#if CORE + /// + /// Gets access to underlying Core graphics path. + /// + internal CoreGraphicsPath _corePath; +#endif + +#if GDI + /// + /// Gets access to underlying GDI+ graphics path. + /// + internal GraphicsPath _gdipPath; +#endif + +#if WPF || NETFX_CORE + /// + /// Gets access to underlying WPF/WinRT path geometry. + /// + internal PathGeometry _pathGeometry; +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathInternals.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathInternals.cs new file mode 100644 index 00000000..bdd4afef --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathInternals.cs @@ -0,0 +1,76 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Provides access to the internal data structures of XGraphicsPath. + /// This class prevents the public interface from pollution with internal functions. + /// + public sealed class XGraphicsPathInternals + { + internal XGraphicsPathInternals(XGraphicsPath path) + { + _path = path; + } + XGraphicsPath _path; + +#if GDI + /// + /// Gets the underlying GDI+ path object. + /// + public GraphicsPath GdiPath + { + get { return _path._gdipPath; } + } +#endif + +#if WPF || NETFX_CORE + /// + /// Gets the underlying WPF path geometry object. + /// + public PathGeometry WpfPath + { + get { return _path._pathGeometry; } + } +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathItem.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathItem.cs new file mode 100644 index 00000000..82b64ad0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsPathItem.cs @@ -0,0 +1,77 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ +#if true_ // unused + /// + /// Represents a segment of a path defined by a type and a set of points. + /// + internal sealed class XGraphicsPathItem + { + public XGraphicsPathItem(XGraphicsPathItemType type) + { + Type = type; + Points = null; + } + +#if GDI + public XGraphicsPathItem(XGraphicsPathItemType type, params PointF[] points) + { + Type = type; + Points = XGraphics.MakeXPointArray(points, 0, points.Length); + } +#endif + + public XGraphicsPathItem(XGraphicsPathItemType type, params XPoint[] points) + { + Type = type; + Points = (XPoint[])points.Clone(); + } + + public XGraphicsPathItem Clone() + { + XGraphicsPathItem item = (XGraphicsPathItem)MemberwiseClone(); + item.Points = (XPoint[])Points.Clone(); + return item; + } + + public XGraphicsPathItemType Type; + public XPoint[] Points; + } +#endif +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsState.cs b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsState.cs new file mode 100644 index 00000000..107caec2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XGraphicsState.cs @@ -0,0 +1,65 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents the internal state of an XGraphics object. + /// This class is used as a handle for restoring the context. + /// + public sealed class XGraphicsState + { + // This class is simply a wrapper of InternalGraphicsState. +#if CORE + internal XGraphicsState() + { } +#endif +#if GDI + internal XGraphicsState(GraphicsState state) + { + GdiState = state; + } + + internal GraphicsState GdiState; +#endif +#if WPF + internal XGraphicsState() + { } +#endif + internal InternalGraphicsState InternalState; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XImage.cs b/src/PDFsharp/src/PdfSharp/Drawing/XImage.cs new file mode 100644 index 00000000..32774bb6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XImage.cs @@ -0,0 +1,1560 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Pdf; +#if CORE +using System.Drawing; +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +#endif +#if NETFX_CORE || UWP +using Windows.UI.Xaml.Media.Imaging; +#endif +using PdfSharp.Drawing.Internal; +using PdfSharp.Internal; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Advanced; + +// WPFHACK +#pragma warning disable 0169 +#pragma warning disable 0649 + +namespace PdfSharp.Drawing +{ + [Flags] + internal enum XImageState + { + UsedInDrawingContext = 0x00000001, + + StateMask = 0x0000FFFF, + } + + /// + /// Defines an object used to draw image files (bmp, png, jpeg, gif) and PDF forms. + /// An abstract base class that provides functionality for the Bitmap and Metafile descended classes. + /// + public class XImage : IDisposable + { + // The hierarchy is adapted to WPF/Silverlight/WinRT + // + // XImage <-- ImageSource + // XForm + // PdfForm + // XBitmapSource <-- BitmapSource + // XBitmapImage <-- BitmapImage + + // ??? + //public bool Disposed + //{ + // get { return _disposed; } + // set { _disposed = value; } + //} + + + /// + /// Initializes a new instance of the class. + /// + protected XImage() + { } + +#if GDI || CORE || WPF + /// + /// Initializes a new instance of the class from an image read by ImageImporter. + /// + /// The image. + /// image + XImage(ImportedImage image) + { + if (image == null) + throw new ArgumentNullException("image"); + + _importedImage = image; + Initialize(); + } +#endif + +#if GDI + /// + /// Initializes a new instance of the class from a GDI+ image. + /// + XImage(Image image) + { + _gdiImage = image; +#if WPF // Is defined in hybrid build. + _wpfImage = ImageHelper.CreateBitmapSource(image); +#endif + Initialize(); + } +#endif + +#if WPF + /// + /// Initializes a new instance of the class from a WPF image. + /// + XImage(BitmapSource image) + { + _wpfImage = image; + Initialize(); + } +#endif + +#if WPF + XImage(Uri uri) + { + //var uriSource = new Uri(@"/WpfApplication1;component/Untitled.png", UriKind.Relative); foo.Source = new BitmapImage(uriSource); + + _wpfImage = BitmapFromUri(uri); + + //throw new NotImplementedException("XImage from Uri."); + // WPF + //Image finalImage = new Image(); + //finalImage.Width = 80; + //...BitmapImage logo = new BitmapImage() + //logo.BeginInit();logo.UriSource = new Uri("pack://application:,,,/ApplicationName;component/Resources/logo.png"); + //logo.EndInit(); + //...finalImage.Source = logo; + } + + /// + /// Creates an BitmapImage from URI. Sets BitmapCacheOption.OnLoad for WPF to prevent image file from being locked. + /// + public static BitmapImage BitmapFromUri(Uri uri) + { +#if !SILVERLIGHT + // Using new BitmapImage(uri) will leave a lock on the file, leading to problems with temporary image files in server environments. + // We use BitmapCacheOption.OnLoad to prevent this lock. + BitmapImage bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.UriSource = uri; + bitmap.CacheOption = BitmapCacheOption.OnLoad; + bitmap.EndInit(); + return bitmap; +#else + return new BitmapImage(uri); +#endif + } +#endif + +#if NETFX_CORE + /// + /// Initializes a new instance of the class from a WinRT image. + /// + XImage(BitmapSource image) + { + _wrtImage = image; + Initialize(); + } +#endif + + // Useful stuff here: http://stackoverflow.com/questions/350027/setting-wpf-image-source-in-code + XImage(string path) + { +#if !NETFX_CORE && !UWP + path = Path.GetFullPath(path); + if (!File.Exists(path)) + throw new FileNotFoundException(PSSR.FileNotFound(path)); + //throw new FileNotFoundException(PSSR.FileNotFound(path), path); +#endif + _path = path; + + //FileStream file = new FileStream(filename, FileMode.Open); + //BitsLength = (int)file.Length; + //Bits = new byte[BitsLength]; + //file.Read(Bits, 0, BitsLength); + //file.Close(); +#if CORE_WITH_GDI || GDI + try + { + Lock.EnterGdiPlus(); + _gdiImage = Image.FromFile(path); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF && !SILVERLIGHT + //BitmapSource.Create() + // BUG: BitmapImage locks the file + //_wpfImage = new BitmapImage(new Uri(path)); // AGHACK + // Suggested change from forum to prevent locking. + _wpfImage = BitmapFromUri(new Uri(path)); +#endif +#if WPF && SILVERLIGHT + //BitmapSource.Create() + // BUG: BitmapImage locks the file + //_wpfImage = new BitmapImage(new Uri(path)); // AGHACK + //Debug-Break.Break(); +#endif + +#if true_ + float vres = image.VerticalResolution; + float hres = image.HorizontalResolution; + SizeF size = image.PhysicalDimension; + int flags = image.Flags; + Size sz = image.Size; + GraphicsUnit units = GraphicsUnit.Millimeter; + RectangleF rect = image.GetBounds(ref units); + int width = image.Width; +#endif + Initialize(); + } + + XImage(Stream stream) + { + // Create a dummy unique path. + _path = "*" + Guid.NewGuid().ToString("B"); + + // TODO: Create a fingerprint of the bytes in the stream to identify identical images. + // TODO: Merge code for CORE_WITH_GDI and GDI. +#if CORE_WITH_GDI + // Create a GDI+ image. + try + { + Lock.EnterGdiPlus(); + _gdiImage = Image.FromStream(stream); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI + // Create a GDI+ image. + try + { + Lock.EnterGdiPlus(); + _gdiImage = Image.FromStream(stream); + } + finally { Lock.ExitGdiPlus(); } +#endif +#if WPF && !SILVERLIGHT + // Create a WPF BitmapImage. + BitmapImage bmi = new BitmapImage(); + bmi.BeginInit(); + bmi.StreamSource = stream; + bmi.EndInit(); + _wpfImage = bmi; +#endif +#if SILVERLIGHT + int length = (int)stream.Length; + stream.Seek(0, SeekOrigin.Begin); + //_bytes = new byte[length]; + //stream.Read(_bytes, 0, length); + //stream.Seek(0, SeekOrigin.Begin); + + // Create a Silverlight BitmapImage. + _wpfImage = new BitmapImage(); + _wpfImage.SetSource(stream); +#endif + +#if true_ + float vres = image.VerticalResolution; + float hres = image.HorizontalResolution; + SizeF size = image.PhysicalDimension; + int flags = image.Flags; + Size sz = image.Size; + GraphicsUnit units = GraphicsUnit.Millimeter; + RectangleF rect = image.GetBounds(ref units); + int width = image.Width; +#endif + // Must assign _stream before Initialize(). + _stream = stream; + Initialize(); + } + +#if GDI //|| CORE +#if UseGdiObjects + /// + /// Implicit conversion from Image to XImage. + /// + public static implicit operator XImage(Image image) + { + return new XImage(image); + } +#endif + + /// + /// Conversion from Image to XImage. + /// + public static XImage FromGdiPlusImage(Image image) + { + return new XImage(image); + } +#endif + +#if WPF + /// + /// Conversion from BitmapSource to XImage. + /// + public static XImage FromBitmapSource(BitmapSource image) + { + return new XImage(image); + } +#endif + +#if NETFX_CORE + /// + /// Conversion from BitmapSource to XImage. + /// + public static XImage FromBitmapSource(BitmapSource image) + { + return new XImage(image); + } +#endif + + /// + /// Creates an image from the specified file. + /// + /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. + public static XImage FromFile(string path) + { + if (PdfReader.TestPdfFile(path) > 0) + return new XPdfForm(path); + return new XImage(path); + } + + /// + /// Creates an image from the specified stream.
+ /// Silverlight supports PNG and JPEG only. + ///
+ /// The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file. + public static XImage FromStream(Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + + if (PdfReader.TestPdfFile(stream) > 0) + return new XPdfForm(stream); + return new XImage(stream); + } + +#if DEBUG +#if CORE || GDI || WPF + /// + /// Creates an image from the specified file. + /// + /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. + /// Uses an platform-independent implementation if set to true. + /// The platform-dependent implementation, if available, will support more image formats. + /// The document used to obtain the options. + internal static XImage FromFile(string path, bool platformIndependent, PdfDocument document) + { + if (!platformIndependent) + return FromFile(path); + + // TODO: Check PDF file. + + ImageImporter ii = ImageImporter.GetImageImporter(); + ImportedImage i = ii.ImportImage(path, document); + + if (i == null) + throw new InvalidOperationException("Unsupported image format."); + + XImage image = new XImage(i); + image._path = path; + return image; + } + + /// + /// Creates an image from the specified stream.
+ /// Silverlight supports PNG and JPEF only. + ///
+ /// The stream containing a BMP, PNG, GIF, JPEG, TIFF, or PDF file. + /// Uses an platform-independent implementation if set to true. + /// The platform-dependent implementation, if available, will support more image formats. + /// The document used to obtain the options. + internal static XImage FromStream(Stream stream, bool platformIndependent, PdfDocument document) + { + if (!platformIndependent) + return FromStream(stream); + + // TODO: Check PDF file. + + ImageImporter ii = ImageImporter.GetImageImporter(); + ImportedImage i = ii.ImportImage(stream, document); + + if (i == null) + throw new InvalidOperationException("Unsupported image format."); + + XImage image = new XImage(i); + image._stream = stream; + return image; + } +#endif +#endif + +#if DEBUG +#if CORE || GDI || WPF + /// + /// Creates an image. + /// + /// The imported image. + [Obsolete("THHO4THHO Internal test code.")] + internal static XImage FromImportedImage(ImportedImage image) + { + if (image == null) + throw new ArgumentNullException("image"); + + return new XImage(image); + } +#endif +#endif + + /// + /// Tests if a file exist. Supports PDF files with page number suffix. + /// + /// The path to a BMP, PNG, GIF, JPEG, TIFF, or PDF file. + public static bool ExistsFile(string path) + { + // Support for "base64:" pseudo protocol is a MigraDoc feature, currently completely implemented in MigraDoc files. TODO: Does support for "base64:" make sense for PDFsharp? Probably not as PDFsharp can handle images from streams. + //if (path.StartsWith("base64:")) // The Image is stored in the string here, so the file exists. + // return true; + + if (PdfReader.TestPdfFile(path) > 0) + return true; +#if !NETFX_CORE && !UWP + return File.Exists(path); +#else + return false; +#endif + } + + internal XImageState XImageState + { + get { return _xImageState; } + set { _xImageState = value; } + } + XImageState _xImageState; + + internal void Initialize() + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + ImportedImageJpeg iiJpeg = _importedImage as ImportedImageJpeg; + // In PDF there are two formats: JPEG and PDF bitmap. + if (iiJpeg != null) + _format = XImageFormat.Jpeg; + else + _format = XImageFormat.Png; + return; + } +#endif + +#if CORE_WITH_GDI + if (_gdiImage != null) + { + // ImageFormat has no overridden Equals function. + string guid; + try + { + Lock.EnterGdiPlus(); + guid = _gdiImage.RawFormat.Guid.ToString("B").ToUpper(); + } + finally + { + Lock.ExitGdiPlus(); + } + + switch (guid) + { + case "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}": // memoryBMP + case "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}": // bmp + case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": // png + _format = XImageFormat.Png; + break; + + case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg + _format = XImageFormat.Jpeg; + break; + + case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif + _format = XImageFormat.Gif; + break; + + case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff + _format = XImageFormat.Tiff; + break; + + case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon + _format = XImageFormat.Icon; + break; + + case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf + case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf + case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif + case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD + case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX + + default: + throw new InvalidOperationException("Unsupported image format."); + } + return; + } +#endif +#if GDI + if (_gdiImage != null) + { + // ImageFormat has no overridden Equals function. + string guid; + try + { + Lock.EnterGdiPlus(); + guid = _gdiImage.RawFormat.Guid.ToString("B").ToUpper(); + } + finally { Lock.ExitGdiPlus(); } + + switch (guid) + { + case "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}": // memoryBMP + case "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}": // bmp + case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": // png + _format = XImageFormat.Png; + break; + + case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg + _format = XImageFormat.Jpeg; + break; + + case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif + _format = XImageFormat.Gif; + break; + + case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff + _format = XImageFormat.Tiff; + break; + + case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon + _format = XImageFormat.Icon; + break; + + case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf + case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf + case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif + case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD + case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX + + default: + throw new InvalidOperationException("Unsupported image format."); + } + return; + } +#endif +#if WPF +#if !SILVERLIGHT + if (_wpfImage != null) + { + //string filename = GetImageFilename(_wpfImage); + // WPF treats all images as images. + // We give JPEG images a special treatment. + // Test if it's a JPEG. + bool isJpeg = IsJpeg; // TestJpeg(filename); + if (isJpeg) + { + _format = XImageFormat.Jpeg; + return; + } + + string pixelFormat = _wpfImage.Format.ToString(); + switch (pixelFormat) + { + case "Bgr32": + case "Bgra32": + case "Pbgra32": + _format = XImageFormat.Png; + break; + + //case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg + // format = XImageFormat.Jpeg; + // break; + + //case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif + case "BlackWhite": + case "Indexed1": + case "Indexed4": + case "Indexed8": + case "Gray8": + _format = XImageFormat.Gif; + break; + + //case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff + // format = XImageFormat.Tiff; + // break; + + //case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon + // format = XImageFormat.Icon; + // break; + + //case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf + //case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf + //case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif + //case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD + //case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX + + default: + Debug.Assert(false, "Unknown pixel format: " + pixelFormat); + _format = XImageFormat.Gif; + break;// throw new InvalidOperationException("Unsupported image format."); + } + } +#else + if (_wpfImage != null) + { + // TODO improve implementation for Silverlight. + + //string pixelFormat = "jpg"; //_wpfImage...Format.ToString(); + //string filename = GetImageFilename(_wpfImage); + // WPF treats all images as images. + // We give JPEG images a special treatment. + // Test if it's a JPEG: + bool isJpeg = true; // IsJpeg; // TestJpeg(filename); + if (isJpeg) + { + _format = XImageFormat.Jpeg; + return; + } + + /* + switch (pixelFormat) + { + case "Bgr32": + case "Bgra32": + case "Pbgra32": + _format = XImageFormat.Png; + break; + + //case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": // jpeg + // format = XImageFormat.Jpeg; + // break; + + //case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": // gif + case "BlackWhite": + case "Indexed1": + case "Indexed4": + case "Indexed8": + case "Gray8": + _format = XImageFormat.Gif; + break; + + //case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": // tiff + // format = XImageFormat.Tiff; + // break; + + //case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": // icon + // format = XImageFormat.Icon; + // break; + + //case "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}": // emf + //case "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}": // wmf + //case "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}": // exif + //case "{B96B3CB3-0728-11D3-9D7B-0000F81EF32E}": // photoCD + //case "{B96B3CB4-0728-11D3-9D7B-0000F81EF32E}": // flashPIX + + default: + Debug.Assert(false, "Unknown pixel format: " + pixelFormat); + _format = XImageFormat.Gif; + break;// throw new InvalidOperationException("Unsupported image format."); + } + */ + } +#endif +#endif + } + +#if WPF + /// + /// Gets the image filename. + /// + /// The bitmap source. + internal static string GetImageFilename(BitmapSource bitmapSource) + { + string filename = bitmapSource.ToString(); + filename = UrlDecodeStringFromStringInternal(filename); + if (filename.StartsWith("file:///")) + filename = filename.Substring(8); // Remove all 3 slashes! + else if (filename.StartsWith("file://")) + filename = filename.Substring(5); // Keep 2 slashes (UNC path) + return filename; + } + + private static string UrlDecodeStringFromStringInternal(string s/*, Encoding e*/) + { + int length = s.Length; + string result = ""; + for (int i = 0; i < length; i++) + { + char ch = s[i]; + if (ch == '+') + { + ch = ' '; + } + else if ((ch == '%') && (i < (length - 2))) + { + if ((s[i + 1] == 'u') && (i < (length - 5))) + { + int num3 = HexToInt(s[i + 2]); + int num4 = HexToInt(s[i + 3]); + int num5 = HexToInt(s[i + 4]); + int num6 = HexToInt(s[i + 5]); + if (((num3 < 0) || (num4 < 0)) || ((num5 < 0) || (num6 < 0))) + { + goto AddByte; + } + ch = (char)((((num3 << 12) | (num4 << 8)) | (num5 << 4)) | num6); + i += 5; + result += ch; + continue; + } + int num7 = HexToInt(s[i + 1]); + int num8 = HexToInt(s[i + 2]); + if ((num7 >= 0) && (num8 >= 0)) + { + byte b = (byte)((num7 << 4) | num8); + i += 2; + result += (char)b; + continue; + } + } + AddByte: + if ((ch & 0xff80) == 0) + { + result += ch; + } + else + { + result += ch; + } + } + return result; + } + + private static int HexToInt(char h) + { + if (h >= '0' && h <= '9') + return (h - '0'); + if (h >= 'a' && h <= 'f') + return ((h - 'a') + 10); + if (h >= 'A' && h <= 'F') + return (h - 'A') + 10; + return -1; + } +#endif + +#if WPF + /// + /// Tests if a file is a JPEG. + /// + /// The filename. + internal static bool TestJpeg(string filename) + { + byte[] imageBits = null; + return ReadJpegFile(filename, 16, ref imageBits); + } + + /// + /// Tests if a file is a JPEG. + /// + /// The filename. + internal static bool TestJpeg(Stream stream) + { + byte[] imageBits = null; + return ReadJpegFile(stream, 16, ref imageBits) == true; + } + + /// + /// Reads the JPEG file. + /// + /// The filename. + /// The maximum count of bytes to be read. + /// The bytes read from the file. + /// False, if file could not be read or is not a JPEG file. + internal static bool ReadJpegFile(string filename, int maxRead, ref byte[] imageBits) + { + if (File.Exists(filename)) + { + FileStream fs = null; + try + { + fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + catch + { + return false; + } + + bool? test = ReadJpegFile(fs, maxRead, ref imageBits); + // Treat test result as definite. + if (test == false || test == true) + { + fs.Close(); + return test.Value; + } + // Test result is maybe. + // Hack: store the file in PDF if extension matches ... + string str = filename.ToLower(); + if (str.EndsWith(".jpg") || str.EndsWith(".jpeg")) + return true; + } + return false; + } + + /// + /// Reads the JPEG file. + /// + /// The stream. + /// The maximum count of bytes to be read. + /// The bytes read from the file. + /// False, if file could not be read or is not a JPEG file. + internal static bool? ReadJpegFile(Stream stream, int maxRead, ref byte[] imageBits) + { + if (!stream.CanSeek) + return false; + stream.Seek(0, SeekOrigin.Begin); + + if (stream.Length < 16) + { + return false; + } + int len = maxRead == -1 ? (int)stream.Length : maxRead; + imageBits = new byte[len]; + stream.Read(imageBits, 0, len); + if (imageBits[0] == 0xff && + imageBits[1] == 0xd8 && + imageBits[2] == 0xff && + imageBits[3] == 0xe0 && + imageBits[6] == 0x4a && + imageBits[7] == 0x46 && + imageBits[8] == 0x49 && + imageBits[9] == 0x46 && + imageBits[10] == 0x0) + { + return true; + } + // TODO: Exif: find JFIF header + if (imageBits[0] == 0xff && + imageBits[1] == 0xd8 && + imageBits[2] == 0xff && + imageBits[3] == 0xe1 /*&& + imageBits[6] == 0x4a && + imageBits[7] == 0x46 && + imageBits[8] == 0x49 && + imageBits[9] == 0x46 && + imageBits[10] == 0x0*/) + { + // Hack: store the file in PDF if extension matches ... + return null; + } + return false; + } +#endif + + /// + /// Under construction + /// + public void Dispose() + { + Dispose(true); + //GC.SuppressFinalize(this); + } + + /// + /// Disposes underlying GDI+ object. + /// + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + _disposed = true; + +#if CORE || GDI || WPF + //if (_importedImage != null) + { + _importedImage = null; + } +#endif + +#if CORE_WITH_GDI || GDI + if (_gdiImage != null) + { + try + { + Lock.EnterGdiPlus(); + _gdiImage.Dispose(); + _gdiImage = null; + } + finally { Lock.ExitGdiPlus(); } + } +#endif +#if WPF + _wpfImage = null; +#endif + } + bool _disposed; + + /// + /// Gets the width of the image. + /// + [Obsolete("Use either PixelWidth or PointWidth. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelWidth, but will become PointWidth in future releases of PDFsharp.")] + public virtual double Width + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + return _importedImage.Information.Width; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Width; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiWidth = _gdiImage.Width; + double wpfWidth = _wpfImage.PixelWidth; + Debug.Assert(gdiWidth == wpfWidth); + return wpfWidth; +#endif + //#if GDI && !WPF + // return _gdiImage.Width; + //#endif +#if WPF && !GDI + return _wpfImage.PixelWidth; +#endif +#if NETFX_CORE || UWP || DNC10 + return 100; +#endif + } + } + + /// + /// Gets the height of the image. + /// + [Obsolete("Use either PixelHeight or PointHeight. Temporarily obsolete because of rearrangements for WPF. Currently same as PixelHeight, but will become PointHeight in future releases of PDFsharp.")] + public virtual double Height + { + get + { +#if CORE_WITH_GDI || GDI || WPF + if (_importedImage != null) + { + return _importedImage.Information.Height; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Height; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiHeight = _gdiImage.Height; + double wpfHeight = _wpfImage.PixelHeight; + Debug.Assert(gdiHeight == wpfHeight); + return wpfHeight; +#endif + //#if GDI && !WPF + // return _gdiImage.Height; + //#endif +#if WPF && !GDI + return _wpfImage.PixelHeight; +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelHeight; +#endif + } + } + +#if CORE || GDI || WPF + /// + /// The factor for conversion from DPM to PointWidth or PointHeight. + /// 72 points per inch, 1000 mm per meter, 25.4 mm per inch => 72 * 1000 / 25.4. + /// + private const decimal FactorDPM72 = 72000 / 25.4m; + + /// + /// The factor for conversion from DPM to PointWidth or PointHeight. + /// 1000 mm per meter, 25.4 mm per inch => 1000 / 25.4. + /// + private const decimal FactorDPM = 1000 / 25.4m; +#endif + + /// + /// Gets the width of the image in point. + /// + public virtual double PointWidth + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + if (_importedImage.Information.HorizontalDPM > 0) + return (double)(_importedImage.Information.Width * FactorDPM72 / _importedImage.Information.HorizontalDPM); + if (_importedImage.Information.HorizontalDPI > 0) + return (double)(_importedImage.Information.Width * 72 / _importedImage.Information.HorizontalDPI); + // Assume 72 DPI if information not available. + return _importedImage.Information.Width; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Width * 72 / _gdiImage.HorizontalResolution; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiWidth = _gdiImage.Width * 72 / _gdiImage.HorizontalResolution; + double wpfWidth = _wpfImage.Width * 72.0 / 96.0; + //Debug.Assert(gdiWidth == wpfWidth); + Debug.Assert(DoubleUtil.AreRoughlyEqual(gdiWidth, wpfWidth, 5)); + return wpfWidth; +#endif + //#if GDI && !WPF + // return _gdiImage.Width * 72 / _gdiImage.HorizontalResolution; + //#endif +#if WPF && !GDI +#if !SILVERLIGHT + Debug.Assert(Math.Abs(_wpfImage.PixelWidth * 72 / _wpfImage.DpiX - _wpfImage.Width * 72.0 / 96.0) < 0.001); + return _wpfImage.Width * 72.0 / 96.0; +#else + // AGHACK + return _wpfImage.PixelWidth * 72 / 96.0; +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + //var wb = new WriteableBitmap(); + //GetImagePropertiesAsync + return 100; +#endif + } + } + + /// + /// Gets the height of the image in point. + /// + public virtual double PointHeight + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + if (_importedImage.Information.VerticalDPM > 0) + return (double)(_importedImage.Information.Height * FactorDPM72 / _importedImage.Information.VerticalDPM); + if (_importedImage.Information.VerticalDPI > 0) + return (double)(_importedImage.Information.Height * 72 / _importedImage.Information.VerticalDPI); + // Assume 72 DPI if information not available. + return _importedImage.Information.Width; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Height * 72 / _gdiImage.HorizontalResolution; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiHeight = _gdiImage.Height * 72 / _gdiImage.HorizontalResolution; + double wpfHeight = _wpfImage.Height * 72.0 / 96.0; + Debug.Assert(DoubleUtil.AreRoughlyEqual(gdiHeight, wpfHeight, 5)); + return wpfHeight; +#endif + //#if GDI && !WPF + // return _gdiImage.Height * 72 / _gdiImage.HorizontalResolution; + //#endif +#if WPF && !GDI +#if !SILVERLIGHT + Debug.Assert(Math.Abs(_wpfImage.PixelHeight * 72 / _wpfImage.DpiY - _wpfImage.Height * 72.0 / 96.0) < 0.001); + return _wpfImage.Height * 72.0 / 96.0; +#else + // AGHACK + return _wpfImage.PixelHeight * 72 / 96.0; +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelHeight; //_gdi Image.Width * 72 / _gdiImage.HorizontalResolution; +#endif + } + } + + /// + /// Gets the width of the image in pixels. + /// + public virtual int PixelWidth + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + return (int)_importedImage.Information.Width; +#endif + +#if CORE_WITH_GDI + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Width; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Width; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + int gdiWidth = _gdiImage.Width; + int wpfWidth = _wpfImage.PixelWidth; + Debug.Assert(gdiWidth == wpfWidth); + return wpfWidth; +#endif + //#if GDI && !WPF + // return _gdiImage.Width; + //#endif +#if WPF && !GDI + return _wpfImage.PixelWidth; +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelWidth; +#endif + } + } + + /// + /// Gets the height of the image in pixels. + /// + public virtual int PixelHeight + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + return (int)_importedImage.Information.Height; +#endif + +#if CORE_WITH_GDI + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Height; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.Height; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + int gdiHeight = _gdiImage.Height; + int wpfHeight = _wpfImage.PixelHeight; + Debug.Assert(gdiHeight == wpfHeight); + return wpfHeight; +#endif + //#if GDI && !WPF + // return _gdiImage.Height; + //#endif +#if WPF && !GDI + return _wpfImage.PixelHeight; +#endif +#if NETFX_CORE || UWP || DNC10 + return _wrtImage.PixelHeight; +#endif + } + } + + /// + /// Gets the size in point of the image. + /// + public virtual XSize Size + { + get { return new XSize(PointWidth, PointHeight); } + } + + /// + /// Gets the horizontal resolution of the image. + /// + public virtual double HorizontalResolution + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + if (_importedImage.Information.HorizontalDPI > 0) + return (double)_importedImage.Information.HorizontalDPI; + if (_importedImage.Information.HorizontalDPM > 0) + return (double)(_importedImage.Information.HorizontalDPM / FactorDPM); + return 72; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.HorizontalResolution; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiResolution = _gdiImage.HorizontalResolution; + double wpfResolution = _wpfImage.PixelWidth * 96.0 / _wpfImage.Width; + Debug.Assert(gdiResolution == wpfResolution); + return wpfResolution; +#endif + //#if GDI && !WPF + // return _gdiImage.HorizontalResolution; + //#endif +#if WPF && !GDI +#if !SILVERLIGHT + return _wpfImage.DpiX; //.PixelWidth * 96.0 / _wpfImage.Width; +#else + // AGHACK + return 96; +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + return 96; +#endif + } + } + + /// + /// Gets the vertical resolution of the image. + /// + public virtual double VerticalResolution + { + get + { +#if CORE || GDI || WPF + if (_importedImage != null) + { + if (_importedImage.Information.VerticalDPI > 0) + return (double)_importedImage.Information.VerticalDPI; + if (_importedImage.Information.VerticalDPM > 0) + return (double)(_importedImage.Information.VerticalDPM / FactorDPM); + return 72; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + try + { + Lock.EnterGdiPlus(); + return _gdiImage.VerticalResolution; + } + finally { Lock.ExitGdiPlus(); } +#endif +#if GDI && WPF + double gdiResolution = _gdiImage.VerticalResolution; + double wpfResolution = _wpfImage.PixelHeight * 96.0 / _wpfImage.Height; + Debug.Assert(gdiResolution == wpfResolution); + return wpfResolution; +#endif + //#if GDI && !WPF + // return _gdiImage.VerticalResolution; + //#endif +#if WPF && !GDI +#if !SILVERLIGHT + return _wpfImage.DpiY; //.PixelHeight * 96.0 / _wpfImage.Height; +#else + // AGHACK + return 96; +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + return 96; +#endif + } + } + + /// + /// Gets or sets a flag indicating whether image interpolation is to be performed. + /// + public virtual bool Interpolate + { + get { return _interpolate; } + set { _interpolate = value; } + } + bool _interpolate = true; + + /// + /// Gets the format of the image. + /// + public XImageFormat Format + { + get { return _format; } + } + XImageFormat _format; + +#if WPF + /// + /// Gets a value indicating whether this image is JPEG. + /// + internal virtual bool IsJpeg + { +#if !SILVERLIGHT + //get { if (!isJpeg.HasValue) InitializeGdiHelper(); return isJpeg.HasValue ? isJpeg.Value : false; } + get + { + if (!_isJpeg.HasValue) + InitializeJpegQuickTest(); + return _isJpeg.HasValue ? _isJpeg.Value : false; + } + //set { isJpeg = value; } +#else + // AGHACK + get { return true; } +#endif + } + private bool? _isJpeg; + + /// + /// Gets a value indicating whether this image is cmyk. + /// + internal virtual bool IsCmyk + { +#if !SILVERLIGHT + get { if (!_isCmyk.HasValue) InitializeGdiHelper(); return _isCmyk.HasValue ? _isCmyk.Value : false; } + //set { isCmyk = value; } +#else + get { return false; } // AGHACK +#endif + } + private bool? _isCmyk; + +#if !SILVERLIGHT + /// + /// Gets the JPEG memory stream (if IsJpeg returns true). + /// + public virtual MemoryStream Memory + { + get + { + if (!_isCmyk.HasValue) InitializeGdiHelper(); + return _memory; + } + //set { memory = value; } + } + MemoryStream _memory; + + /// + /// Determines if an image is JPEG w/o creating an Image object. + /// + private void InitializeJpegQuickTest() + { + if (_stream != null) + _isJpeg = TestJpeg(_stream); + else + _isJpeg = TestJpeg(GetImageFilename(_wpfImage)); + } + + /// + /// Initializes the GDI helper. + /// We use GDI+ to detect if image is JPEG. + /// If so, we also determine if it's CMYK and we read the image bytes. + /// + private void InitializeGdiHelper() + { + if (!_isCmyk.HasValue) + { + try + { + string imageFilename = GetImageFilename(_wpfImage); + // To reduce exceptions, check if file exists. + if (!string.IsNullOrEmpty(imageFilename) && File.Exists(imageFilename)) + { + MemoryStream memory = new MemoryStream(); + using (FileStream file = new FileStream(imageFilename, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + byte[] bytes = new byte[file.Length]; + file.Read(bytes, 0, (int)file.Length); + memory.Write(bytes, 0, (int)file.Length); + memory.Seek(0, SeekOrigin.Begin); + } + InitializeJpegHelper(memory); + } + else if (_stream != null) + { + MemoryStream memory = new MemoryStream(); + // If we have a stream, copy data from the stream. + if (_stream != null && _stream.CanSeek) + { + _stream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[32 * 1024]; // 32K buffer. + int bytesRead; + while ((bytesRead = _stream.Read(buffer, 0, buffer.Length)) > 0) + { + memory.Write(buffer, 0, bytesRead); + } + InitializeJpegHelper(memory); + } + } + } + catch { } + } + } + + private void InitializeJpegHelper(MemoryStream memory) + { + using (System.Drawing.Image image = new System.Drawing.Bitmap(memory)) + { + string guid = image.RawFormat.Guid.ToString("B").ToUpper(); + _isJpeg = guid == "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}"; + _isCmyk = (image.Flags & + ((int)System.Drawing.Imaging.ImageFlags.ColorSpaceCmyk | (int)System.Drawing.Imaging.ImageFlags.ColorSpaceYcck)) != 0; + if (_isJpeg.Value) + { + //_memory = new MemoryStream(); + //image.Save(_memory, System.Drawing.Imaging.ImageFormat.Jpeg); + if ((int)memory.Length != 0) + { + _memory = memory; + } + else + { + _memory = null; + } + } + } + } +#endif +#endif + +#if DEBUG_ + // TEST + internal void CreateAllImages(string name) + { + if (image != null) + { + image.Save(name + ".bmp", ImageFormat.Bmp); + image.Save(name + ".emf", ImageFormat.Emf); + image.Save(name + ".exif", ImageFormat.Exif); + image.Save(name + ".gif", ImageFormat.Gif); + image.Save(name + ".ico", ImageFormat.Icon); + image.Save(name + ".jpg", ImageFormat.Jpeg); + image.Save(name + ".png", ImageFormat.Png); + image.Save(name + ".tif", ImageFormat.Tiff); + image.Save(name + ".wmf", ImageFormat.Wmf); + image.Save(name + "2.bmp", ImageFormat.MemoryBmp); + } + } +#endif + + internal void AssociateWithGraphics(XGraphics gfx) + { + if (_associatedGraphics != null) + throw new InvalidOperationException("XImage already associated with XGraphics."); + _associatedGraphics = null; + } + + internal void DisassociateWithGraphics() + { + if (_associatedGraphics == null) + throw new InvalidOperationException("XImage not associated with XGraphics."); + _associatedGraphics.DisassociateImage(); + + Debug.Assert(_associatedGraphics == null); + } + + internal void DisassociateWithGraphics(XGraphics gfx) + { + if (_associatedGraphics != gfx) + throw new InvalidOperationException("XImage not associated with XGraphics."); + _associatedGraphics = null; + } + + internal XGraphics AssociatedGraphics + { + get { return _associatedGraphics; } + set { _associatedGraphics = value; } + } + XGraphics _associatedGraphics; + +#if CORE || GDI || WPF + internal ImportedImage _importedImage; +#endif + +#if CORE_WITH_GDI || GDI + internal Image _gdiImage; +#endif +#if WPF + internal BitmapSource _wpfImage; +#if SILVERLIGHT + //internal byte[] _bytes; +#endif +#endif +#if NETFX_CORE || UWP || DNC10 + internal BitmapSource _wrtImage; +#endif + + /// + /// If path starts with '*' the image is created from a stream and the path is a GUID. + /// + internal string _path; + + /// + /// Contains a reference to the original stream if image was created from a stream. + /// + internal Stream _stream; + + /// + /// Cache PdfImageTable.ImageSelector to speed up finding the right PdfImage + /// if this image is used more than once. + /// + internal PdfImageTable.ImageSelector _selector; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XImageFormat.cs b/src/PDFsharp/src/PdfSharp/Drawing/XImageFormat.cs new file mode 100644 index 00000000..e9685753 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XImageFormat.cs @@ -0,0 +1,141 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the format of the image. + /// + public sealed class XImageFormat + { + XImageFormat(Guid guid) + { + _guid = guid; + } + + internal Guid Guid + { + get { return _guid; } + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + public override bool Equals(object obj) + { + XImageFormat format = obj as XImageFormat; + if (format == null) + return false; + return _guid == format._guid; + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + return _guid.GetHashCode(); + } + + /// + /// Gets the Portable Network Graphics (PNG) image format. + /// + public static XImageFormat Png + { + get { return _png; } + } + + /// + /// Gets the Graphics Interchange Format (GIF) image format. + /// + public static XImageFormat Gif + { + get { return _gif; } + } + + /// + /// Gets the Joint Photographic Experts Group (JPEG) image format. + /// + public static XImageFormat Jpeg + { + get { return _jpeg; } + } + + /// + /// Gets the Tag Image File Format (TIFF) image format. + /// + public static XImageFormat Tiff + { + get { return _tiff; } + } + + /// + /// Gets the Portable Document Format (PDF) image format + /// + public static XImageFormat Pdf + { + get { return _pdf; } + } + + /// + /// Gets the Windows icon image format. + /// + public static XImageFormat Icon + { + get { return _icon; } + } + + readonly Guid _guid; + + // Guids used in GDI+ + //ImageFormat.memoryBMP = new ImageFormat(new Guid("{b96b3caa-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.bmp = new ImageFormat(new Guid("{b96b3cab-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.emf = new ImageFormat(new Guid("{b96b3cac-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.wmf = new ImageFormat(new Guid("{b96b3cad-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.jpeg = new ImageFormat(new Guid("{b96b3cae-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.png = new ImageFormat(new Guid("{b96b3caf-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.gif = new ImageFormat(new Guid("{b96b3cb0-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.tiff = new ImageFormat(new Guid("{b96b3cb1-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.exif = new ImageFormat(new Guid("{b96b3cb2-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.photoCD = new ImageFormat(new Guid("{b96b3cb3-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.flashPIX = new ImageFormat(new Guid("{b96b3cb4-0728-11d3-9d7b-0000f81ef32e}")); + //ImageFormat.icon = new ImageFormat(new Guid("{b96b3cb5-0728-11d3-9d7b-0000f81ef32e}")); + + // #??? Why Guids? + private static readonly XImageFormat _png = new XImageFormat(new Guid("{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}")); + private static readonly XImageFormat _gif = new XImageFormat(new Guid("{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}")); + private static readonly XImageFormat _jpeg = new XImageFormat(new Guid("{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}")); + private static readonly XImageFormat _tiff = new XImageFormat(new Guid("{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}")); + private static readonly XImageFormat _icon = new XImageFormat(new Guid("{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}")); + // not GDI+ conform + private static readonly XImageFormat _pdf = new XImageFormat(new Guid("{84570158-DBF0-4C6B-8368-62D6A3CA76E0}")); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XKnownColorTable.cs b/src/PDFsharp/src/PdfSharp/Drawing/XKnownColorTable.cs new file mode 100644 index 00000000..020f51c6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XKnownColorTable.cs @@ -0,0 +1,222 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + internal class XKnownColorTable + { + internal static uint[] ColorTable; + + public static uint KnownColorToArgb(XKnownColor color) + { + if (ColorTable == null) + InitColorTable(); + if (color <= XKnownColor.YellowGreen) + return ColorTable[(int)color]; + return 0; + } + + public static bool IsKnownColor(uint argb) + { + for (int idx = 0; idx < ColorTable.Length; idx++) + { + if (ColorTable[idx] == argb) + return true; + } + return false; + } + + public static XKnownColor GetKnownColor(uint argb) + { + for (int idx = 0; idx < ColorTable.Length; idx++) + { + if (ColorTable[idx] == argb) + return (XKnownColor)idx; + } + return (XKnownColor)(-1); + } + + private static void InitColorTable() + { + // Same values as in GDI+ and System.Windows.Media.XColors + // Note that Magenta is the same as Fuchsia and Zyan is the same as Aqua. + uint[] colors = new uint[141]; + colors[0] = 0xFFF0F8FF; // AliceBlue + colors[1] = 0xFFFAEBD7; // AntiqueWhite + colors[2] = 0xFF00FFFF; // Aqua + colors[3] = 0xFF7FFFD4; // Aquamarine + colors[4] = 0xFFF0FFFF; // Azure + colors[5] = 0xFFF5F5DC; // Beige + colors[6] = 0xFFFFE4C4; // Bisque + colors[7] = 0xFF000000; // Black + colors[8] = 0xFFFFEBCD; // BlanchedAlmond + colors[9] = 0xFF0000FF; // Blue + colors[10] = 0xFF8A2BE2; // BlueViolet + colors[11] = 0xFFA52A2A; // Brown + colors[12] = 0xFFDEB887; // BurlyWood + colors[13] = 0xFF5F9EA0; // CadetBlue + colors[14] = 0xFF7FFF00; // Chartreuse + colors[15] = 0xFFD2691E; // Chocolate + colors[16] = 0xFFFF7F50; // Coral + colors[17] = 0xFF6495ED; // CornflowerBlue + colors[18] = 0xFFFFF8DC; // Cornsilk + colors[19] = 0xFFDC143C; // Crimson + colors[20] = 0xFF00FFFF; // Cyan + colors[21] = 0xFF00008B; // DarkBlue + colors[22] = 0xFF008B8B; // DarkCyan + colors[23] = 0xFFB8860B; // DarkGoldenrod + colors[24] = 0xFFA9A9A9; // DarkGray + colors[25] = 0xFF006400; // DarkGreen + colors[26] = 0xFFBDB76B; // DarkKhaki + colors[27] = 0xFF8B008B; // DarkMagenta + colors[28] = 0xFF556B2F; // DarkOliveGreen + colors[29] = 0xFFFF8C00; // DarkOrange + colors[30] = 0xFF9932CC; // DarkOrchid + colors[31] = 0xFF8B0000; // DarkRed + colors[32] = 0xFFE9967A; // DarkSalmon + colors[33] = 0xFF8FBC8B; // DarkSeaGreen + colors[34] = 0xFF483D8B; // DarkSlateBlue + colors[35] = 0xFF2F4F4F; // DarkSlateGray + colors[36] = 0xFF00CED1; // DarkTurquoise + colors[37] = 0xFF9400D3; // DarkViolet + colors[38] = 0xFFFF1493; // DeepPink + colors[39] = 0xFF00BFFF; // DeepSkyBlue + colors[40] = 0xFF696969; // DimGray + colors[41] = 0xFF1E90FF; // DodgerBlue + colors[42] = 0xFFB22222; // Firebrick + colors[43] = 0xFFFFFAF0; // FloralWhite + colors[44] = 0xFF228B22; // ForestGreen + colors[45] = 0xFFFF00FF; // Fuchsia + colors[46] = 0xFFDCDCDC; // Gainsboro + colors[47] = 0xFFF8F8FF; // GhostWhite + colors[48] = 0xFFFFD700; // Gold + colors[49] = 0xFFDAA520; // Goldenrod + colors[50] = 0xFF808080; // Gray + colors[51] = 0xFF008000; // Green + colors[52] = 0xFFADFF2F; // GreenYellow + colors[53] = 0xFFF0FFF0; // Honeydew + colors[54] = 0xFFFF69B4; // HotPink + colors[55] = 0xFFCD5C5C; // IndianRed + colors[56] = 0xFF4B0082; // Indigo + colors[57] = 0xFFFFFFF0; // Ivory + colors[58] = 0xFFF0E68C; // Khaki + colors[59] = 0xFFE6E6FA; // Lavender + colors[60] = 0xFFFFF0F5; // LavenderBlush + colors[61] = 0xFF7CFC00; // LawnGreen + colors[62] = 0xFFFFFACD; // LemonChiffon + colors[63] = 0xFFADD8E6; // LightBlue + colors[64] = 0xFFF08080; // LightCoral + colors[65] = 0xFFE0FFFF; // LightCyan + colors[66] = 0xFFFAFAD2; // LightGoldenrodYellow + colors[67] = 0xFFD3D3D3; // LightGray + colors[68] = 0xFF90EE90; // LightGreen + colors[69] = 0xFFFFB6C1; // LightPink + colors[70] = 0xFFFFA07A; // LightSalmon + colors[71] = 0xFF20B2AA; // LightSeaGreen + colors[72] = 0xFF87CEFA; // LightSkyBlue + colors[73] = 0xFF778899; // LightSlateGray + colors[74] = 0xFFB0C4DE; // LightSteelBlue + colors[75] = 0xFFFFFFE0; // LightYellow + colors[76] = 0xFF00FF00; // Lime + colors[77] = 0xFF32CD32; // LimeGreen + colors[78] = 0xFFFAF0E6; // Linen + colors[79] = 0xFFFF00FF; // Magenta + colors[80] = 0xFF800000; // Maroon + colors[81] = 0xFF66CDAA; // MediumAquamarine + colors[82] = 0xFF0000CD; // MediumBlue + colors[83] = 0xFFBA55D3; // MediumOrchid + colors[84] = 0xFF9370DB; // MediumPurple + colors[85] = 0xFF3CB371; // MediumSeaGreen + colors[86] = 0xFF7B68EE; // MediumSlateBlue + colors[87] = 0xFF00FA9A; // MediumSpringGreen + colors[88] = 0xFF48D1CC; // MediumTurquoise + colors[89] = 0xFFC71585; // MediumVioletRed + colors[90] = 0xFF191970; // MidnightBlue + colors[91] = 0xFFF5FFFA; // MintCream + colors[92] = 0xFFFFE4E1; // MistyRose + colors[93] = 0xFFFFE4B5; // Moccasin + colors[94] = 0xFFFFDEAD; // NavajoWhite + colors[95] = 0xFF000080; // Navy + colors[96] = 0xFFFDF5E6; // OldLace + colors[97] = 0xFF808000; // Olive + colors[98] = 0xFF6B8E23; // OliveDrab + colors[99] = 0xFFFFA500; // Orange + colors[100] = 0xFFFF4500; // OrangeRed + colors[101] = 0xFFDA70D6; // Orchid + colors[102] = 0xFFEEE8AA; // PaleGoldenrod + colors[103] = 0xFF98FB98; // PaleGreen + colors[104] = 0xFFAFEEEE; // PaleTurquoise + colors[105] = 0xFFDB7093; // PaleVioletRed + colors[106] = 0xFFFFEFD5; // PapayaWhip + colors[107] = 0xFFFFDAB9; // PeachPuff + colors[108] = 0xFFCD853F; // Peru + colors[109] = 0xFFFFC0CB; // Pink + colors[110] = 0xFFDDA0DD; // Plum + colors[111] = 0xFFB0E0E6; // PowderBlue + colors[112] = 0xFF800080; // Purple + colors[113] = 0xFFFF0000; // Red + colors[114] = 0xFFBC8F8F; // RosyBrown + colors[115] = 0xFF4169E1; // RoyalBlue + colors[116] = 0xFF8B4513; // SaddleBrown + colors[117] = 0xFFFA8072; // Salmon + colors[118] = 0xFFF4A460; // SandyBrown + colors[119] = 0xFF2E8B57; // SeaGreen + colors[120] = 0xFFFFF5EE; // SeaShell + colors[121] = 0xFFA0522D; // Sienna + colors[122] = 0xFFC0C0C0; // Silver + colors[123] = 0xFF87CEEB; // SkyBlue + colors[124] = 0xFF6A5ACD; // SlateBlue + colors[125] = 0xFF708090; // SlateGray + colors[126] = 0xFFFFFAFA; // Snow + colors[127] = 0xFF00FF7F; // SpringGreen + colors[128] = 0xFF4682B4; // SteelBlue + colors[129] = 0xFFD2B48C; // Tan + colors[130] = 0xFF008080; // Teal + colors[131] = 0xFFD8BFD8; // Thistle + colors[132] = 0xFFFF6347; // Tomato + colors[133] = 0x00FFFFFF; // Transparent + colors[134] = 0xFF40E0D0; // Turquoise + colors[135] = 0xFFEE82EE; // Violet + colors[136] = 0xFFF5DEB3; // Wheat + colors[137] = 0xFFFFFFFF; // White + colors[138] = 0xFFF5F5F5; // WhiteSmoke + colors[139] = 0xFFFFFF00; // Yellow + colors[140] = 0xFF9ACD32; // YellowGreen + + ColorTable = colors; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XLinearGradientBrush.cs b/src/PDFsharp/src/PdfSharp/Drawing/XLinearGradientBrush.cs new file mode 100644 index 00000000..4cfb6209 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XLinearGradientBrush.cs @@ -0,0 +1,393 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; +using PdfSharp.Internal; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiLinearGradientBrush = System.Drawing.Drawing2D.LinearGradientBrush; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +using WpfBrush = System.Windows.Media.Brush; +using WpfLinearGradientBrush = System.Windows.Media.LinearGradientBrush; +#endif +#if UWP +using Windows.UI; +using Windows.UI.Xaml.Media; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +#endif + +// ReSharper disable RedundantNameQualifier because it is required for hybrid build + +namespace PdfSharp.Drawing +{ + /// + /// Defines a Brush with a linear gradient. + /// + public sealed class XLinearGradientBrush : XGradientBrush + { +#if GDI + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(System.Drawing.Point point1, System.Drawing.Point point2, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), color1, color2) + { } + + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(PointF point1, PointF point2, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), color1, color2) + { } +#endif + +#if WPF + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(SysPoint point1, SysPoint point2, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), color1, color2) + { } +#endif + + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(XPoint point1, XPoint point2, XColor color1, XColor color2) + { + _point1 = point1; + _point2 = point2; + _color1 = color1; + _color2 = color2; + } + +#if GDI + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(Rectangle rect, XColor color1, XColor color2, XLinearGradientMode linearGradientMode) + : this(new XRect(rect), color1, color2, linearGradientMode) + { } + + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(RectangleF rect, XColor color1, XColor color2, XLinearGradientMode linearGradientMode) + : this(new XRect(rect), color1, color2, linearGradientMode) + { } +#endif + +#if WPF + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(Rect rect, XColor color1, XColor color2, XLinearGradientMode linearGradientMode) + : this(new XRect(rect), color1, color2, linearGradientMode) + { } +#endif + + /// + /// Initializes a new instance of the class. + /// + public XLinearGradientBrush(XRect rect, XColor color1, XColor color2, XLinearGradientMode linearGradientMode) + { + if (!Enum.IsDefined(typeof(XLinearGradientMode), linearGradientMode)) + throw new InvalidEnumArgumentException("linearGradientMode", (int)linearGradientMode, typeof(XLinearGradientMode)); + + if (rect.Width == 0 || rect.Height == 0) + throw new ArgumentException("Invalid rectangle.", "rect"); + + _useRect = true; + _color1 = color1; + _color2 = color2; + _rect = rect; + _linearGradientMode = linearGradientMode; + } + + // TODO: + //public XLinearGradientBrush(Rectangle rect, XColor color1, XColor color2, double angle); + //public XLinearGradientBrush(RectangleF rect, XColor color1, XColor color2, double angle); + //public XLinearGradientBrush(Rectangle rect, XColor color1, XColor color2, double angle, bool isAngleScaleable); + //public XLinearGradientBrush(RectangleF rect, XColor color1, XColor color2, double angle, bool isAngleScaleable); + //public XLinearGradientBrush(RectangleF rect, XColor color1, XColor color2, double angle, bool isAngleScaleable); + + //private Blend _GetBlend(); + //private ColorBlend _GetInterpolationColors(); + //private XColor[] _GetLinearColors(); + //private RectangleF _GetRectangle(); + //private Matrix _GetTransform(); + //private WrapMode _GetWrapMode(); + //private void _SetBlend(Blend blend); + //private void _SetInterpolationColors(ColorBlend blend); + //private void _SetLinearColors(XColor color1, XColor color2); + //private void _SetTransform(Matrix matrix); + //private void _SetWrapMode(WrapMode wrapMode); + + //public override object Clone(); + + /// + /// Gets or sets an XMatrix that defines a local geometric transform for this LinearGradientBrush. + /// + public XMatrix Transform + { + get { return _matrix; } + set { _matrix = value; } + } + + /// + /// Translates the brush with the specified offset. + /// + public void TranslateTransform(double dx, double dy) + { + _matrix.TranslatePrepend(dx, dy); + } + + /// + /// Translates the brush with the specified offset. + /// + public void TranslateTransform(double dx, double dy, XMatrixOrder order) + { + _matrix.Translate(dx, dy, order); + } + + /// + /// Scales the brush with the specified scalars. + /// + public void ScaleTransform(double sx, double sy) + { + _matrix.ScalePrepend(sx, sy); + } + + /// + /// Scales the brush with the specified scalars. + /// + public void ScaleTransform(double sx, double sy, XMatrixOrder order) + { + _matrix.Scale(sx, sy, order); + } + + /// + /// Rotates the brush with the specified angle. + /// + public void RotateTransform(double angle) + { + _matrix.RotatePrepend(angle); + } + + /// + /// Rotates the brush with the specified angle. + /// + public void RotateTransform(double angle, XMatrixOrder order) + { + _matrix.Rotate(angle, order); + } + + /// + /// Multiply the brush transformation matrix with the specified matrix. + /// + public void MultiplyTransform(XMatrix matrix) + { + _matrix.Prepend(matrix); + } + + /// + /// Multiply the brush transformation matrix with the specified matrix. + /// + public void MultiplyTransform(XMatrix matrix, XMatrixOrder order) + { + _matrix.Multiply(matrix, order); + } + + /// + /// Resets the brush transformation matrix with identity matrix. + /// + public void ResetTransform() + { + _matrix = new XMatrix(); + } + + //public void SetBlendTriangularShape(double focus); + //public void SetBlendTriangularShape(double focus, double scale); + //public void SetSigmaBellShape(double focus); + //public void SetSigmaBellShape(double focus, double scale); + +#if GDI + internal override System.Drawing.Brush RealizeGdiBrush() + { + //if (dirty) + //{ + // if (brush == null) + // brush = new SolidBrush(color.ToGdiColor()); + // else + // { + // brush.Color = color.ToGdiColor(); + // } + // dirty = false; + //} + + // TODO: use dirty to optimize code + GdiLinearGradientBrush brush; + try + { + Lock.EnterGdiPlus(); + if (_useRect) + { + brush = new GdiLinearGradientBrush(_rect.ToRectangleF(), + _color1.ToGdiColor(), _color2.ToGdiColor(), (LinearGradientMode)_linearGradientMode); + } + else + { + brush = new GdiLinearGradientBrush( + _point1.ToPointF(), _point2.ToPointF(), + _color1.ToGdiColor(), _color2.ToGdiColor()); + } + if (!_matrix.IsIdentity) + brush.Transform = _matrix.ToGdiMatrix(); + //brush.WrapMode = WrapMode.Clamp; + } + finally { Lock.ExitGdiPlus(); } + return brush; + } +#endif + +#if WPF + internal override WpfBrush RealizeWpfBrush() + { + //if (dirty) + //{ + // if (brush == null) + // brush = new SolidBrush(color.ToGdiColor()); + // else + // { + // brush.Color = color.ToGdiColor(); + // } + // dirty = false; + //} + + WpfLinearGradientBrush brush; + if (_useRect) + { +#if !SILVERLIGHT + brush = new WpfLinearGradientBrush(_color1.ToWpfColor(), _color2.ToWpfColor(), new SysPoint(0, 0), new SysPoint(1, 1));// rect.TopLeft, this.rect.BottomRight); + //brush = new System.Drawing.Drawing2D.LinearGradientBrush(rect.ToRectangleF(), + // color1.ToGdiColor(), color2.ToGdiColor(), (LinearGradientMode)linearGradientMode); +#else + GradientStop gs1 = new GradientStop(); + gs1.Color = _color1.ToWpfColor(); + gs1.Offset = 0; + + GradientStop gs2 = new GradientStop(); + gs2.Color = _color2.ToWpfColor(); + gs2.Offset = 1; + + GradientStopCollection gsc = new GradientStopCollection(); + gsc.Add(gs1); + gsc.Add(gs2); + + brush = new LinearGradientBrush(gsc, 0); + brush.StartPoint = new Point(0, 0); + brush.EndPoint = new Point(1, 1); +#endif + } + else + { +#if !SILVERLIGHT + brush = new System.Windows.Media.LinearGradientBrush(_color1.ToWpfColor(), _color2.ToWpfColor(), _point1, _point2); + //brush = new System.Drawing.Drawing2D.LinearGradientBrush( + // point1.ToPointF(), point2.ToPointF(), + // color1.ToGdiColor(), color2.ToGdiColor()); +#else + GradientStop gs1 = new GradientStop(); + gs1.Color = _color1.ToWpfColor(); + gs1.Offset = 0; + + GradientStop gs2 = new GradientStop(); + gs2.Color = _color2.ToWpfColor(); + gs2.Offset = 1; + + GradientStopCollection gsc = new GradientStopCollection(); + gsc.Add(gs1); + gsc.Add(gs2); + + brush = new LinearGradientBrush(gsc, 0); + brush.StartPoint = _point1; + brush.EndPoint = _point2; +#endif + } + if (!_matrix.IsIdentity) + { +#if !SILVERLIGHT + brush.Transform = new MatrixTransform(_matrix.ToWpfMatrix()); +#else + MatrixTransform transform = new MatrixTransform(); + transform.Matrix = _matrix.ToWpfMatrix(); + brush.Transform = transform; +#endif + } + return brush; + } +#endif + +#if UWP + internal override ICanvasBrush RealizeCanvasBrush() + { + ICanvasBrush brush; + + brush = new CanvasSolidColorBrush(CanvasDevice.GetSharedDevice(), Colors.RoyalBlue); + + return brush; + } +#endif + + //public Blend Blend { get; set; } + //public bool GammaCorrection { get; set; } + //public ColorBlend InterpolationColors { get; set; } + //public XColor[] LinearColors { get; set; } + //public RectangleF Rectangle { get; } + //public WrapMode WrapMode { get; set; } + //private bool interpolationColorsWasSet; + + internal bool _useRect; + internal XPoint _point1, _point2; + internal XColor _color1, _color2; + internal XRect _rect; + internal XLinearGradientMode _linearGradientMode; + + internal XMatrix _matrix; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XMatrix.cs b/src/PDFsharp/src/PdfSharp/Drawing/XMatrix.cs new file mode 100644 index 00000000..4c5d6771 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XMatrix.cs @@ -0,0 +1,1557 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif +#if !EDF_CORE +using PdfSharp.Internal; +#else +using PdfSharp.Internal; +#endif + +// ReSharper disable RedundantNameQualifier +#if !EDF_CORE +namespace PdfSharp.Drawing +#else +namespace Edf.Drawing +#endif +{ + /// + /// Represents a 3-by-3 matrix that represents an affine 2D transformation. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + [Serializable, StructLayout(LayoutKind.Sequential)] //, TypeConverter(typeof(MatrixConverter)), ValueSerializer(typeof(MatrixValueSerializer))] + public struct XMatrix : IFormattable + { + [Flags] + internal enum XMatrixTypes + { + Identity = 0, + Translation = 1, + Scaling = 2, + Unknown = 4 + } + + /// + /// Initializes a new instance of the XMatrix struct. + /// + public XMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY) + { + _m11 = m11; + _m12 = m12; + _m21 = m21; + _m22 = m22; + _offsetX = offsetX; + _offsetY = offsetY; + _type = XMatrixTypes.Unknown; + //_padding = 0; + DeriveMatrixType(); + } + + /// + /// Gets the identity matrix. + /// + public static XMatrix Identity + { + get { return s_identity; } + } + + /// + /// Sets this matrix into an identity matrix. + /// + public void SetIdentity() + { + _type = XMatrixTypes.Identity; + } + + /// + /// Gets a value indicating whether this matrix instance is the identity matrix. + /// + public bool IsIdentity + { + get + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (_type == XMatrixTypes.Identity) + return true; + if (_m11 == 1.0 && _m12 == 0 && _m21 == 0 && _m22 == 1.0 && _offsetX == 0 && _offsetY == 0) + { + _type = XMatrixTypes.Identity; + return true; + } + return false; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + } + + ///// + ///// Gets an array of double values that represents the elements of this matrix. + ///// + //[Obsolete("Use GetElements().")] + //public double[] Elements + //{ + // get { return GetElements(); } + //} + + /// + /// Gets an array of double values that represents the elements of this matrix. + /// + public double[] GetElements() + { + if (_type == XMatrixTypes.Identity) + return new double[] { 1, 0, 0, 1, 0, 0 }; + return new double[] { _m11, _m12, _m21, _m22, _offsetX, _offsetY }; + } + + /// + /// Multiplies two matrices. + /// + public static XMatrix operator *(XMatrix trans1, XMatrix trans2) + { + MatrixHelper.MultiplyMatrix(ref trans1, ref trans2); + return trans1; + } + + /// + /// Multiplies two matrices. + /// + public static XMatrix Multiply(XMatrix trans1, XMatrix trans2) + { + MatrixHelper.MultiplyMatrix(ref trans1, ref trans2); + return trans1; + } + + /// + /// Appends the specified matrix to this matrix. + /// + public void Append(XMatrix matrix) + { + this *= matrix; + } + + /// + /// Prepends the specified matrix to this matrix. + /// + public void Prepend(XMatrix matrix) + { + this = matrix * this; + } + + /// + /// Appends the specified matrix to this matrix. + /// + [Obsolete("Use Append.")] + public void Multiply(XMatrix matrix) + { + Append(matrix); + } + + /// + /// Prepends the specified matrix to this matrix. + /// + [Obsolete("Use Prepend.")] + public void MultiplyPrepend(XMatrix matrix) + { + Prepend(matrix); + } + + /// + /// Multiplies this matrix with the specified matrix. + /// + public void Multiply(XMatrix matrix, XMatrixOrder order) + { + if (_type == XMatrixTypes.Identity) + this = CreateIdentity(); + + // Must use properties, the fields can be invalid if the matrix is identity matrix. + double t11 = M11; + double t12 = M12; + double t21 = M21; + double t22 = M22; + double tdx = OffsetX; + double tdy = OffsetY; + + if (order == XMatrixOrder.Append) + { + _m11 = t11 * matrix.M11 + t12 * matrix.M21; + _m12 = t11 * matrix.M12 + t12 * matrix.M22; + _m21 = t21 * matrix.M11 + t22 * matrix.M21; + _m22 = t21 * matrix.M12 + t22 * matrix.M22; + _offsetX = tdx * matrix.M11 + tdy * matrix.M21 + matrix.OffsetX; + _offsetY = tdx * matrix.M12 + tdy * matrix.M22 + matrix.OffsetY; + } + else + { + _m11 = t11 * matrix.M11 + t21 * matrix.M12; + _m12 = t12 * matrix.M11 + t22 * matrix.M12; + _m21 = t11 * matrix.M21 + t21 * matrix.M22; + _m22 = t12 * matrix.M21 + t22 * matrix.M22; + _offsetX = t11 * matrix.OffsetX + t21 * matrix.OffsetY + tdx; + _offsetY = t12 * matrix.OffsetX + t22 * matrix.OffsetY + tdy; + } + DeriveMatrixType(); + } + + /// + /// Appends a translation of the specified offsets to this matrix. + /// + [Obsolete("Use TranslateAppend or TranslatePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void Translate(double offsetX, double offsetY) + { + throw new InvalidOperationException("Temporarily out of order."); + //if (_type == XMatrixTypes.Identity) + //{ + // SetMatrix(1.0, 0, 0, 1.0, offsetX, offsetY, XMatrixTypes.Translation); + //} + //else if (_type == XMatrixTypes.Unknown) + //{ + // _offsetX += offsetX; + // _offsetY += offsetY; + //} + //else + //{ + // _offsetX += offsetX; + // _offsetY += offsetY; + // _type |= XMatrixTypes.Translation; + //} + } + + /// + /// Appends a translation of the specified offsets to this matrix. + /// + public void TranslateAppend(double offsetX, double offsetY) // TODO: will become default + { + if (_type == XMatrixTypes.Identity) + { + SetMatrix(1, 0, 0, 1, offsetX, offsetY, XMatrixTypes.Translation); + } + else if (_type == XMatrixTypes.Unknown) + { + _offsetX += offsetX; + _offsetY += offsetY; + } + else + { + _offsetX += offsetX; + _offsetY += offsetY; + _type |= XMatrixTypes.Translation; + } + } + + /// + /// Prepends a translation of the specified offsets to this matrix. + /// + public void TranslatePrepend(double offsetX, double offsetY) + { + this = CreateTranslation(offsetX, offsetY) * this; + } + + /// + /// Translates the matrix with the specified offsets. + /// + public void Translate(double offsetX, double offsetY, XMatrixOrder order) + { + if (_type == XMatrixTypes.Identity) + this = CreateIdentity(); + + if (order == XMatrixOrder.Append) + { + _offsetX += offsetX; + _offsetY += offsetY; + } + else + { + _offsetX += offsetX * _m11 + offsetY * _m21; + _offsetY += offsetX * _m12 + offsetY * _m22; + } + DeriveMatrixType(); + } + + /// + /// Appends the specified scale vector to this matrix. + /// + [Obsolete("Use ScaleAppend or ScalePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void Scale(double scaleX, double scaleY) + { + this = CreateScaling(scaleX, scaleY) * this; + } + + /// + /// Appends the specified scale vector to this matrix. + /// + public void ScaleAppend(double scaleX, double scaleY) // TODO: will become default + { + this *= CreateScaling(scaleX, scaleY); + } + + /// + /// Prepends the specified scale vector to this matrix. + /// + public void ScalePrepend(double scaleX, double scaleY) + { + this = CreateScaling(scaleX, scaleY) * this; + } + + /// + /// Scales the matrix with the specified scalars. + /// + public void Scale(double scaleX, double scaleY, XMatrixOrder order) + { + if (_type == XMatrixTypes.Identity) + this = CreateIdentity(); + + if (order == XMatrixOrder.Append) + { + _m11 *= scaleX; + _m12 *= scaleY; + _m21 *= scaleX; + _m22 *= scaleY; + _offsetX *= scaleX; + _offsetY *= scaleY; + } + else + { + _m11 *= scaleX; + _m12 *= scaleX; + _m21 *= scaleY; + _m22 *= scaleY; + } + DeriveMatrixType(); + } + + /// + /// Scales the matrix with the specified scalar. + /// + [Obsolete("Use ScaleAppend or ScalePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + // ReSharper disable InconsistentNaming + public void Scale(double scaleXY) + // ReSharper restore InconsistentNaming + { + throw new InvalidOperationException("Temporarily out of order."); + //Scale(scaleXY, scaleXY, XMatrixOrder.Prepend); + } + + /// + /// Appends the specified scale vector to this matrix. + /// + // ReSharper disable InconsistentNaming + public void ScaleAppend(double scaleXY) + // ReSharper restore InconsistentNaming + { + Scale(scaleXY, scaleXY, XMatrixOrder.Append); + } + + /// + /// Prepends the specified scale vector to this matrix. + /// + // ReSharper disable InconsistentNaming + public void ScalePrepend(double scaleXY) + // ReSharper restore InconsistentNaming + { + Scale(scaleXY, scaleXY, XMatrixOrder.Prepend); + } + + /// + /// Scales the matrix with the specified scalar. + /// + // ReSharper disable InconsistentNaming + public void Scale(double scaleXY, XMatrixOrder order) + // ReSharper restore InconsistentNaming + { + Scale(scaleXY, scaleXY, order); + } + + /// + /// Function is obsolete. + /// + [Obsolete("Use ScaleAtAppend or ScaleAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void ScaleAt(double scaleX, double scaleY, double centerX, double centerY) + { + throw new InvalidOperationException("Temporarily out of order."); + //this *= CreateScaling(scaleX, scaleY, centerX, centerY); + } + + /// + /// Apppends the specified scale about the specified point of this matrix. + /// + public void ScaleAtAppend(double scaleX, double scaleY, double centerX, double centerY) // TODO: will become default + { + this *= CreateScaling(scaleX, scaleY, centerX, centerY); + } + + /// + /// Prepends the specified scale about the specified point of this matrix. + /// + public void ScaleAtPrepend(double scaleX, double scaleY, double centerX, double centerY) + { + this = CreateScaling(scaleX, scaleY, centerX, centerY) * this; + } + + /// + /// Function is obsolete. + /// + [Obsolete("Use RotateAppend or RotatePrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void Rotate(double angle) + { + throw new InvalidOperationException("Temporarily out of order."); + //angle = angle % 360.0; + //this *= CreateRotationRadians(angle * Const.Deg2Rad); + } + + /// + /// Appends a rotation of the specified angle to this matrix. + /// + public void RotateAppend(double angle) // TODO: will become default Rotate + { + angle = angle % 360.0; + this *= CreateRotationRadians(angle * Const.Deg2Rad); + } + + /// + /// Prepends a rotation of the specified angle to this matrix. + /// + public void RotatePrepend(double angle) + { + angle = angle % 360.0; + this = CreateRotationRadians(angle * Const.Deg2Rad) * this; + } + + /// + /// Rotates the matrix with the specified angle. + /// + public void Rotate(double angle, XMatrixOrder order) + { + if (_type == XMatrixTypes.Identity) + this = CreateIdentity(); + + angle = angle * Const.Deg2Rad; + double cos = Math.Cos(angle); + double sin = Math.Sin(angle); + if (order == XMatrixOrder.Append) + { + double t11 = _m11; + double t12 = _m12; + double t21 = _m21; + double t22 = _m22; + double tdx = _offsetX; + double tdy = _offsetY; + _m11 = t11 * cos - t12 * sin; + _m12 = t11 * sin + t12 * cos; + _m21 = t21 * cos - t22 * sin; + _m22 = t21 * sin + t22 * cos; + _offsetX = tdx * cos - tdy * sin; + _offsetY = tdx * sin + tdy * cos; + } + else + { + double t11 = _m11; + double t12 = _m12; + double t21 = _m21; + double t22 = _m22; + _m11 = t11 * cos + t21 * sin; + _m12 = t12 * cos + t22 * sin; + _m21 = -t11 * sin + t21 * cos; + _m22 = -t12 * sin + t22 * cos; + } + DeriveMatrixType(); + } + + /// + /// Function is obsolete. + /// + [Obsolete("Use RotateAtAppend or RotateAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void RotateAt(double angle, double centerX, double centerY) + { + throw new InvalidOperationException("Temporarily out of order."); + //angle = angle % 360.0; + //this *= CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY); + } + + /// + /// Appends a rotation of the specified angle at the specified point to this matrix. + /// + public void RotateAtAppend(double angle, double centerX, double centerY) // TODO: will become default + { + angle = angle % 360.0; + this *= CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY); + } + + /// + /// Prepends a rotation of the specified angle at the specified point to this matrix. + /// + public void RotateAtPrepend(double angle, double centerX, double centerY) + { + angle = angle % 360.0; + this = CreateRotationRadians(angle * Const.Deg2Rad, centerX, centerY) * this; + } + + /// + /// Rotates the matrix with the specified angle at the specified point. + /// + [Obsolete("Use RotateAtAppend or RotateAtPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void RotateAt(double angle, XPoint point) + { + throw new InvalidOperationException("Temporarily out of order."); + //RotateAt(angle, point, XMatrixOrder.Prepend); + } + + /// + /// Appends a rotation of the specified angle at the specified point to this matrix. + /// + public void RotateAtAppend(double angle, XPoint point) + { + RotateAt(angle, point, XMatrixOrder.Append); + } + + /// + /// Prepends a rotation of the specified angle at the specified point to this matrix. + /// + public void RotateAtPrepend(double angle, XPoint point) + { + RotateAt(angle, point, XMatrixOrder.Prepend); + } + + /// + /// Rotates the matrix with the specified angle at the specified point. + /// + public void RotateAt(double angle, XPoint point, XMatrixOrder order) + { + if (order == XMatrixOrder.Append) + { + angle = angle % 360.0; + this *= CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y); + + //Translate(point.X, point.Y, order); + //Rotate(angle, order); + //Translate(-point.X, -point.Y, order); + } + else + { + angle = angle % 360.0; + this = CreateRotationRadians(angle * Const.Deg2Rad, point.X, point.Y) * this; + } + DeriveMatrixType(); + } + + /// + /// Function is obsolete. + /// + [Obsolete("Use ShearAppend or ShearPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void Shear(double shearX, double shearY) + { + throw new InvalidOperationException("Temporarily out of order."); + //Shear(shearX, shearY, XMatrixOrder.Prepend); + } + + /// + /// Appends a skew of the specified degrees in the x and y dimensions to this matrix. + /// + public void ShearAppend(double shearX, double shearY) // TODO: will become default + { + Shear(shearX, shearY, XMatrixOrder.Append); + } + + /// + /// Prepends a skew of the specified degrees in the x and y dimensions to this matrix. + /// + public void ShearPrepend(double shearX, double shearY) + { + Shear(shearX, shearY, XMatrixOrder.Prepend); + } + + /// + /// Shears the matrix with the specified scalars. + /// + public void Shear(double shearX, double shearY, XMatrixOrder order) + { + if (_type == XMatrixTypes.Identity) + this = CreateIdentity(); + + double t11 = _m11; + double t12 = _m12; + double t21 = _m21; + double t22 = _m22; + double tdx = _offsetX; + double tdy = _offsetY; + if (order == XMatrixOrder.Append) + { + _m11 += shearX * t12; + _m12 += shearY * t11; + _m21 += shearX * t22; + _m22 += shearY * t21; + _offsetX += shearX * tdy; + _offsetY += shearY * tdx; + } + else + { + _m11 += shearY * t21; + _m12 += shearY * t22; + _m21 += shearX * t11; + _m22 += shearX * t12; + } + DeriveMatrixType(); + } + + /// + /// Function is obsolete. + /// + [Obsolete("Use SkewAppend or SkewPrepend explicitly, because in GDI+ and WPF the defaults are contrary.", true)] + public void Skew(double skewX, double skewY) + { + throw new InvalidOperationException("Temporarily out of order."); + //skewX = skewX % 360.0; + //skewY = skewY % 360.0; + //this *= CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad); + } + + /// + /// Appends a skew of the specified degrees in the x and y dimensions to this matrix. + /// + public void SkewAppend(double skewX, double skewY) + { + skewX = skewX % 360.0; + skewY = skewY % 360.0; + this *= CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad); + } + + /// + /// Prepends a skew of the specified degrees in the x and y dimensions to this matrix. + /// + public void SkewPrepend(double skewX, double skewY) + { + skewX = skewX % 360.0; + skewY = skewY % 360.0; + this = CreateSkewRadians(skewX * Const.Deg2Rad, skewY * Const.Deg2Rad) * this; + } + + /// + /// Transforms the specified point by this matrix and returns the result. + /// + public XPoint Transform(XPoint point) + { + double x = point.X; + double y = point.Y; + MultiplyPoint(ref x, ref y); + return new XPoint(x, y); + } + + /// + /// Transforms the specified points by this matrix. + /// + public void Transform(XPoint[] points) + { + if (points != null) + { + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + double x = points[idx].X; + double y = points[idx].Y; + MultiplyPoint(ref x, ref y); + points[idx].X = x; + points[idx].Y = y; + } + } + } + + /// + /// Multiplies all points of the specified array with the this matrix. + /// + public void TransformPoints(XPoint[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + if (IsIdentity) + return; + + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + double x = points[idx].X; + double y = points[idx].Y; + points[idx].X = x * _m11 + y * _m21 + _offsetX; + points[idx].Y = x * _m12 + y * _m22 + _offsetY; + } + } + +#if GDI + /// + /// Multiplies all points of the specified array with the this matrix. + /// + public void TransformPoints(System.Drawing.Point[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + if (IsIdentity) + return; + + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + double x = points[idx].X; + double y = points[idx].Y; + points[idx].X = (int)(x * _m11 + y * _m21 + _offsetX); + points[idx].Y = (int)(x * _m12 + y * _m22 + _offsetY); + } + } +#endif + +#if WPF + /// + /// Transforms an array of points. + /// + public void TransformPoints(System.Windows.Point[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + if (IsIdentity) + return; + + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + double x = points[idx].X; + double y = points[idx].Y; + points[idx].X = (int)(x * _m11 + y * _m21 + _offsetX); + points[idx].Y = (int)(x * _m12 + y * _m22 + _offsetY); + } + } +#endif + + /// + /// Transforms the specified vector by this Matrix and returns the result. + /// + public XVector Transform(XVector vector) + { + double x = vector.X; + double y = vector.Y; + MultiplyVector(ref x, ref y); + return new XVector(x, y); + } + + /// + /// Transforms the specified vectors by this matrix. + /// + public void Transform(XVector[] vectors) + { + if (vectors != null) + { + int count = vectors.Length; + for (int idx = 0; idx < count; idx++) + { + double x = vectors[idx].X; + double y = vectors[idx].Y; + MultiplyVector(ref x, ref y); + vectors[idx].X = x; + vectors[idx].Y = y; + } + } + } + +#if GDI + /// + /// Multiplies all vectors of the specified array with the this matrix. The translation elements + /// of this matrix (third row) are ignored. + /// + public void TransformVectors(PointF[] points) + { + if (points == null) + throw new ArgumentNullException("points"); + + if (IsIdentity) + return; + + int count = points.Length; + for (int idx = 0; idx < count; idx++) + { + double x = points[idx].X; + double y = points[idx].Y; + points[idx].X = (float)(x * _m11 + y * _m21 + _offsetX); + points[idx].Y = (float)(x * _m12 + y * _m22 + _offsetY); + } + } +#endif + + /// + /// Gets the determinant of this matrix. + /// + public double Determinant + { + get + { + switch (_type) + { + case XMatrixTypes.Identity: + case XMatrixTypes.Translation: + return 1.0; + + case XMatrixTypes.Scaling: + case XMatrixTypes.Scaling | XMatrixTypes.Translation: + return _m11 * _m22; + } + return (_m11 * _m22) - (_m12 * _m21); + } + } + + /// + /// Gets a value that indicates whether this matrix is invertible. + /// + public bool HasInverse + { + get { return !DoubleUtil.IsZero(Determinant); } + } + + /// + /// Inverts the matrix. + /// + public void Invert() + { + double determinant = Determinant; + if (DoubleUtil.IsZero(determinant)) + throw new InvalidOperationException("NotInvertible"); //SR.Get(SRID.Transform_NotInvertible, new object[0])); + + switch (_type) + { + case XMatrixTypes.Identity: + break; + + case XMatrixTypes.Translation: + _offsetX = -_offsetX; + _offsetY = -_offsetY; + return; + + case XMatrixTypes.Scaling: + _m11 = 1.0 / _m11; + _m22 = 1.0 / _m22; + return; + + case XMatrixTypes.Scaling | XMatrixTypes.Translation: + _m11 = 1.0 / _m11; + _m22 = 1.0 / _m22; + _offsetX = -_offsetX * _m11; + _offsetY = -_offsetY * _m22; + return; + + default: + { + double detInvers = 1.0 / determinant; + SetMatrix(_m22 * detInvers, -_m12 * detInvers, -_m21 * detInvers, _m11 * detInvers, (_m21 * _offsetY - _offsetX * _m22) * detInvers, (_offsetX * _m12 - _m11 * _offsetY) * detInvers, XMatrixTypes.Unknown); + break; + } + } + } + + /// + /// Gets or sets the value of the first row and first column of this matrix. + /// + public double M11 + { + get + { + if (_type == XMatrixTypes.Identity) + return 1.0; + return _m11; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(value, 0, 0, 1, 0, 0, XMatrixTypes.Scaling); + else + { + _m11 = value; + if (_type != XMatrixTypes.Unknown) + _type |= XMatrixTypes.Scaling; + } + } + } + + /// + /// Gets or sets the value of the first row and second column of this matrix. + /// + public double M12 + { + get + { + if (_type == XMatrixTypes.Identity) + return 0; + return _m12; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(1, value, 0, 1, 0, 0, XMatrixTypes.Unknown); + else + { + _m12 = value; + _type = XMatrixTypes.Unknown; + } + } + } + + /// + /// Gets or sets the value of the second row and first column of this matrix. + /// + public double M21 + { + get + { + if (_type == XMatrixTypes.Identity) + return 0; + return _m21; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(1, 0, value, 1, 0, 0, XMatrixTypes.Unknown); + else + { + _m21 = value; + _type = XMatrixTypes.Unknown; + } + } + } + + /// + /// Gets or sets the value of the second row and second column of this matrix. + /// + public double M22 + { + get + { + if (_type == XMatrixTypes.Identity) + return 1.0; + return _m22; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(1, 0, 0, value, 0, 0, XMatrixTypes.Scaling); + else + { + _m22 = value; + if (_type != XMatrixTypes.Unknown) + _type |= XMatrixTypes.Scaling; + } + } + } + + /// + /// Gets or sets the value of the third row and first column of this matrix. + /// + public double OffsetX + { + get + { + if (_type == XMatrixTypes.Identity) + return 0; + return _offsetX; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(1, 0, 0, 1, value, 0, XMatrixTypes.Translation); + else + { + _offsetX = value; + if (_type != XMatrixTypes.Unknown) + _type |= XMatrixTypes.Translation; + } + } + } + + /// + /// Gets or sets the value of the third row and second column of this matrix. + /// + public double OffsetY + { + get + { + if (_type == XMatrixTypes.Identity) + return 0; + return _offsetY; + } + set + { + if (_type == XMatrixTypes.Identity) + SetMatrix(1, 0, 0, 1, 0, value, XMatrixTypes.Translation); + else + { + _offsetY = value; + if (_type != XMatrixTypes.Unknown) + _type |= XMatrixTypes.Translation; + } + } + } + +#if GDI +//#if UseGdiObjects + /// + /// Converts this matrix to a System.Drawing.Drawing2D.Matrix object. + /// + public System.Drawing.Drawing2D.Matrix ToGdiMatrix() + { + if (IsIdentity) + return new System.Drawing.Drawing2D.Matrix(); + + return new System.Drawing.Drawing2D.Matrix((float)_m11, (float)_m12, (float)_m21, (float)_m22, + (float)_offsetX, (float)_offsetY); + } +//#endif +#endif + +#if WPF + /// Converts this matrix to a System.Windows.Media.Matrix object. + /// + /// + public System.Windows.Media.Matrix ToWpfMatrix() + { + return (System.Windows.Media.Matrix)this; + //return new System.Windows.Media.Matrix(_m11, _m12, _m21, _m22, _offsetX, _offsetY); + } +#endif + +#if GDI + /// + /// Explicitly converts a XMatrix to a Matrix. + /// + public static explicit operator System.Drawing.Drawing2D.Matrix(XMatrix matrix) + { + if (matrix.IsIdentity) + return new System.Drawing.Drawing2D.Matrix(); + + return new System.Drawing.Drawing2D.Matrix( + (float)matrix._m11, (float)matrix._m12, + (float)matrix._m21, (float)matrix._m22, + (float)matrix._offsetX, (float)matrix._offsetY); + } +#endif + +#if WPF + /// + /// Explicitly converts an XMatrix to a Matrix. + /// + public static explicit operator System.Windows.Media.Matrix(XMatrix matrix) + { + if (matrix.IsIdentity) + return new System.Windows.Media.Matrix(); + + return new System.Windows.Media.Matrix( + matrix._m11, matrix._m12, + matrix._m21, matrix._m22, + matrix._offsetX, matrix._offsetY); + } +#endif + +#if GDI + /// + /// Implicitly converts a Matrix to an XMatrix. + /// + public static implicit operator XMatrix(System.Drawing.Drawing2D.Matrix matrix) + { + float[] elements = matrix.Elements; + return new XMatrix(elements[0], elements[1], elements[2], elements[3], elements[4], elements[5]); + } +#endif + +#if WPF + /// + /// Implicitly converts a Matrix to an XMatrix. + /// + public static implicit operator XMatrix(System.Windows.Media.Matrix matrix) + { + return new XMatrix(matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY); + } +#endif + + /// + /// Determines whether the two matrices are equal. + /// + public static bool operator ==(XMatrix matrix1, XMatrix matrix2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity) + return (matrix1.IsIdentity == matrix2.IsIdentity); + + return matrix1.M11 == matrix2.M11 && matrix1.M12 == matrix2.M12 && matrix1.M21 == matrix2.M21 && matrix1.M22 == matrix2.M22 && + matrix1.OffsetX == matrix2.OffsetX && matrix1.OffsetY == matrix2.OffsetY; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Determines whether the two matrices are not equal. + /// + public static bool operator !=(XMatrix matrix1, XMatrix matrix2) + { + return !(matrix1 == matrix2); + } + + /// + /// Determines whether the two matrices are equal. + /// + public static bool Equals(XMatrix matrix1, XMatrix matrix2) + { + if (matrix1.IsDistinguishedIdentity || matrix2.IsDistinguishedIdentity) + return matrix1.IsIdentity == matrix2.IsIdentity; + + return matrix1.M11.Equals(matrix2.M11) && matrix1.M12.Equals(matrix2.M12) && + matrix1.M21.Equals(matrix2.M21) && matrix1.M22.Equals(matrix2.M22) && + matrix1.OffsetX.Equals(matrix2.OffsetX) && matrix1.OffsetY.Equals(matrix2.OffsetY); + } + + /// + /// Determines whether this matrix is equal to the specified object. + /// + public override bool Equals(object o) + { + if (!(o is XMatrix)) + return false; + return Equals(this, (XMatrix)o); + } + + /// + /// Determines whether this matrix is equal to the specified matrix. + /// + public bool Equals(XMatrix value) + { + return Equals(this, value); + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + if (IsDistinguishedIdentity) + return 0; + return M11.GetHashCode() ^ M12.GetHashCode() ^ M21.GetHashCode() ^ M22.GetHashCode() ^ OffsetX.GetHashCode() ^ OffsetY.GetHashCode(); + } + + /// + /// Parses a matrix from a string. + /// + public static XMatrix Parse(string source) + { + IFormatProvider cultureInfo = CultureInfo.InvariantCulture; //.GetCultureInfo("en-us"); + TokenizerHelper helper = new TokenizerHelper(source, cultureInfo); + string str = helper.NextTokenRequired(); + XMatrix identity = str == "Identity" ? Identity : new XMatrix( + Convert.ToDouble(str, cultureInfo), + Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), + Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), + Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), + Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), + Convert.ToDouble(helper.NextTokenRequired(), cultureInfo)); + helper.LastTokenRequired(); + return identity; + } + + /// + /// Converts this XMatrix to a human readable string. + /// + public override string ToString() + { + return ConvertToString(null, null); + } + + /// + /// Converts this XMatrix to a human readable string. + /// + public string ToString(IFormatProvider provider) + { + return ConvertToString(null, provider); + } + + /// + /// Converts this XMatrix to a human readable string. + /// + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + if (IsIdentity) + return "Identity"; + + char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider); + provider = provider ?? CultureInfo.InvariantCulture; + // ReSharper disable FormatStringProblem + return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", + new object[] { numericListSeparator, _m11, _m12, _m21, _m22, _offsetX, _offsetY }); + // ReSharper restore FormatStringProblem + } + + internal void MultiplyVector(ref double x, ref double y) + { + switch (_type) + { + case XMatrixTypes.Identity: + case XMatrixTypes.Translation: + return; + + case XMatrixTypes.Scaling: + case XMatrixTypes.Scaling | XMatrixTypes.Translation: + x *= _m11; + y *= _m22; + return; + } + double d1 = y * _m21; + double d2 = x * _m12; + x *= _m11; + x += d1; + y *= _m22; + y += d2; + } + + internal void MultiplyPoint(ref double x, ref double y) + { + switch (_type) + { + case XMatrixTypes.Identity: + return; + + case XMatrixTypes.Translation: + x += _offsetX; + y += _offsetY; + return; + + case XMatrixTypes.Scaling: + x *= _m11; + y *= _m22; + return; + + case (XMatrixTypes.Scaling | XMatrixTypes.Translation): + x *= _m11; + x += _offsetX; + y *= _m22; + y += _offsetY; + return; + } + double d1 = (y * _m21) + _offsetX; + double d2 = (x * _m12) + _offsetY; + x *= _m11; + x += d1; + y *= _m22; + y += d2; + } + + internal static XMatrix CreateTranslation(double offsetX, double offsetY) + { + XMatrix matrix = new XMatrix(); + matrix.SetMatrix(1, 0, 0, 1, offsetX, offsetY, XMatrixTypes.Translation); + return matrix; + } + + internal static XMatrix CreateRotationRadians(double angle) + { + return CreateRotationRadians(angle, 0, 0); + } + + internal static XMatrix CreateRotationRadians(double angle, double centerX, double centerY) + { + XMatrix matrix = new XMatrix(); + double sin = Math.Sin(angle); + double cos = Math.Cos(angle); + double offsetX = (centerX * (1.0 - cos)) + (centerY * sin); + double offsetY = (centerY * (1.0 - cos)) - (centerX * sin); + matrix.SetMatrix(cos, sin, -sin, cos, offsetX, offsetY, XMatrixTypes.Unknown); + return matrix; + } + + internal static XMatrix CreateScaling(double scaleX, double scaleY) + { + XMatrix matrix = new XMatrix(); + matrix.SetMatrix(scaleX, 0, 0, scaleY, 0, 0, XMatrixTypes.Scaling); + return matrix; + } + + internal static XMatrix CreateScaling(double scaleX, double scaleY, double centerX, double centerY) + { + XMatrix matrix = new XMatrix(); + matrix.SetMatrix(scaleX, 0, 0, scaleY, centerX - scaleX * centerX, centerY - scaleY * centerY, XMatrixTypes.Scaling | XMatrixTypes.Translation); + return matrix; + } + + internal static XMatrix CreateSkewRadians(double skewX, double skewY, double centerX, double centerY) + { + XMatrix matrix = new XMatrix(); + matrix.Append(CreateTranslation(-centerX, -centerY)); + matrix.Append(new XMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0)); + matrix.Append(CreateTranslation(centerX, centerY)); + return matrix; + } + + internal static XMatrix CreateSkewRadians(double skewX, double skewY) + { + XMatrix matrix = new XMatrix(); + matrix.SetMatrix(1, Math.Tan(skewY), Math.Tan(skewX), 1, 0, 0, XMatrixTypes.Unknown); + return matrix; + } + + static XMatrix CreateIdentity() + { + XMatrix matrix = new XMatrix(); + matrix.SetMatrix(1, 0, 0, 1, 0, 0, XMatrixTypes.Identity); + return matrix; + } + + /// + /// Sets the matrix. + /// + void SetMatrix(double m11, double m12, double m21, double m22, double offsetX, double offsetY, XMatrixTypes type) + { + _m11 = m11; + _m12 = m12; + _m21 = m21; + _m22 = m22; + _offsetX = offsetX; + _offsetY = offsetY; + _type = type; + } + + void DeriveMatrixType() + { + // ReSharper disable CompareOfFloatsByEqualityOperator + _type = XMatrixTypes.Identity; + if (_m12 != 0 || _m21 != 0) + { + _type = XMatrixTypes.Unknown; + } + else + { + if (_m11 != 1 || _m22 != 1) + _type = XMatrixTypes.Scaling; + + if (_offsetX != 0 || _offsetY != 0) + _type |= XMatrixTypes.Translation; + + if ((_type & (XMatrixTypes.Scaling | XMatrixTypes.Translation)) == XMatrixTypes.Identity) + _type = XMatrixTypes.Identity; + } + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + bool IsDistinguishedIdentity + { + get { return (_type == XMatrixTypes.Identity); } + } + + // Keep the fields private and force using the properties. + // This prevents using m11 and m22 by mistake when the matrix is identity. + double _m11; + double _m12; + double _m21; + double _m22; + double _offsetX; + double _offsetY; + XMatrixTypes _type; + static readonly XMatrix s_identity = CreateIdentity(); + + /// + /// Internal matrix helper. + /// + internal static class MatrixHelper + { + // Fast mutiplication taking matrix type into account. Reflectored from WPF. + internal static void MultiplyMatrix(ref XMatrix matrix1, ref XMatrix matrix2) + { + XMatrixTypes type1 = matrix1._type; + XMatrixTypes type2 = matrix2._type; + if (type2 != XMatrixTypes.Identity) + { + if (type1 == XMatrixTypes.Identity) + matrix1 = matrix2; + else if (type2 == XMatrixTypes.Translation) + { + matrix1._offsetX += matrix2._offsetX; + matrix1._offsetY += matrix2._offsetY; + if (type1 != XMatrixTypes.Unknown) + matrix1._type |= XMatrixTypes.Translation; + } + else if (type1 == XMatrixTypes.Translation) + { + double num = matrix1._offsetX; + double num2 = matrix1._offsetY; + matrix1 = matrix2; + matrix1._offsetX = num * matrix2._m11 + num2 * matrix2._m21 + matrix2._offsetX; + matrix1._offsetY = num * matrix2._m12 + num2 * matrix2._m22 + matrix2._offsetY; + if (type2 == XMatrixTypes.Unknown) + matrix1._type = XMatrixTypes.Unknown; + else + matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation; + } + else + { + switch ((((int)type1) << 4) | (int)type2) + { + case 0x22: + matrix1._m11 *= matrix2._m11; + matrix1._m22 *= matrix2._m22; + return; + + case 0x23: + matrix1._m11 *= matrix2._m11; + matrix1._m22 *= matrix2._m22; + matrix1._offsetX = matrix2._offsetX; + matrix1._offsetY = matrix2._offsetY; + matrix1._type = XMatrixTypes.Scaling | XMatrixTypes.Translation; + return; + + case 0x24: + case 0x34: + case 0x42: + case 0x43: + case 0x44: + matrix1 = new XMatrix( + matrix1._m11 * matrix2._m11 + matrix1._m12 * matrix2._m21, + matrix1._m11 * matrix2._m12 + matrix1._m12 * matrix2._m22, + matrix1._m21 * matrix2._m11 + matrix1._m22 * matrix2._m21, + matrix1._m21 * matrix2._m12 + matrix1._m22 * matrix2._m22, + matrix1._offsetX * matrix2._m11 + matrix1._offsetY * matrix2._m21 + matrix2._offsetX, + matrix1._offsetX * matrix2._m12 + matrix1._offsetY * matrix2._m22 + matrix2._offsetY); + return; + + case 50: + matrix1._m11 *= matrix2._m11; + matrix1._m22 *= matrix2._m22; + matrix1._offsetX *= matrix2._m11; + matrix1._offsetY *= matrix2._m22; + return; + + case 0x33: + matrix1._m11 *= matrix2._m11; + matrix1._m22 *= matrix2._m22; + matrix1._offsetX = matrix2._m11 * matrix1._offsetX + matrix2._offsetX; + matrix1._offsetY = matrix2._m22 * matrix1._offsetY + matrix2._offsetY; + return; + } + } + } + } + + internal static void PrependOffset(ref XMatrix matrix, double offsetX, double offsetY) + { + if (matrix._type == XMatrixTypes.Identity) + { + matrix = new XMatrix(1, 0, 0, 1, offsetX, offsetY); + matrix._type = XMatrixTypes.Translation; + } + else + { + matrix._offsetX += (matrix._m11 * offsetX) + (matrix._m21 * offsetY); + matrix._offsetY += (matrix._m12 * offsetX) + (matrix._m22 * offsetY); + if (matrix._type != XMatrixTypes.Unknown) + matrix._type |= XMatrixTypes.Translation; + } + } + + internal static void TransformRect(ref XRect rect, ref XMatrix matrix) + { + if (!rect.IsEmpty) + { + XMatrixTypes type = matrix._type; + if (type != XMatrixTypes.Identity) + { + if ((type & XMatrixTypes.Scaling) != XMatrixTypes.Identity) + { + rect.X *= matrix._m11; + rect.Y *= matrix._m22; + rect.Width *= matrix._m11; + rect.Height *= matrix._m22; + if (rect.Width < 0) + { + rect.X += rect.Width; + rect.Width = -rect.Width; + } + if (rect.Height < 0) + { + rect.Y += rect.Height; + rect.Height = -rect.Height; + } + } + if ((type & XMatrixTypes.Translation) != XMatrixTypes.Identity) + { + rect.X += matrix._offsetX; + rect.Y += matrix._offsetY; + } + if (type == XMatrixTypes.Unknown) + { + XPoint point1 = matrix.Transform(rect.TopLeft); + XPoint point2 = matrix.Transform(rect.TopRight); + XPoint point3 = matrix.Transform(rect.BottomRight); + XPoint point4 = matrix.Transform(rect.BottomLeft); + rect.X = Math.Min(Math.Min(point1.X, point2.X), Math.Min(point3.X, point4.X)); + rect.Y = Math.Min(Math.Min(point1.Y, point2.Y), Math.Min(point3.Y, point4.Y)); + rect.Width = Math.Max(Math.Max(point1.X, point2.X), Math.Max(point3.X, point4.X)) - rect.X; + rect.Height = Math.Max(Math.Max(point1.Y, point2.Y), Math.Max(point3.Y, point4.Y)) - rect.Y; + } + } + } + } + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + if (IsIdentity) + return "matrix=(Identity)"; + + const string format = Config.SignificantFigures7; + + // Calculate the angle in degrees. + XPoint point = new XMatrix(_m11, _m12, _m21, _m22, 0, 0).Transform(new XPoint(1, 0)); + double φ = Math.Atan2(point.Y, point.X) / Const.Deg2Rad; + return String.Format(CultureInfo.InvariantCulture, + "matrix=({0:" + format + "}, {1:" + format + "}, {2:" + format + "}, {3:" + format + "}, {4:" + format + "}, {5:" + format + "}), φ={6:0.0#########}°", + _m11, _m12, _m21, _m22, _offsetX, _offsetY, φ); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XPdfForm.cs b/src/PDFsharp/src/PdfSharp/Drawing/XPdfForm.cs new file mode 100644 index 00000000..2f2f2278 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XPdfForm.cs @@ -0,0 +1,415 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.IO; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Internal; +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Drawing +{ + /// + /// Represents a so called 'PDF form external object', which is typically an imported page of an external + /// PDF document. XPdfForm objects are used like images to draw an existing PDF page of an external + /// document in the current document. XPdfForm objects can only be placed in PDF documents. If you try + /// to draw them using a XGraphics based on an GDI+ context no action is taken if no placeholder image + /// is specified. Otherwise the place holder is drawn. + /// + public class XPdfForm : XForm + { + /// + /// Initializes a new instance of the XPdfForm class from the specified path to an external PDF document. + /// Although PDFsharp internally caches XPdfForm objects it is recommended to reuse XPdfForm objects + /// in your code and change the PageNumber property if more than one page is needed form the external + /// document. Furthermore, because XPdfForm can occupy very much memory, it is recommended to + /// dispose XPdfForm objects if not needed anymore. + /// + internal XPdfForm(string path) + { + int pageNumber; + path = ExtractPageNumber(path, out pageNumber); + +#if !NETFX_CORE + path = Path.GetFullPath(path); + if (!File.Exists(path)) + throw new FileNotFoundException(PSSR.FileNotFound(path)); +#endif + + if (PdfReader.TestPdfFile(path) == 0) + throw new ArgumentException("The specified file has no valid PDF file header.", "path"); + + _path = path; + if (pageNumber != 0) + PageNumber = pageNumber; + } + + /// + /// Initializes a new instance of the class from a stream. + /// + /// The stream. + internal XPdfForm(Stream stream) + { + // Create a dummy unique path + _path = "*" + Guid.NewGuid().ToString("B"); + + if (PdfReader.TestPdfFile(stream) == 0) + throw new ArgumentException("The specified stream has no valid PDF file header.", "stream"); + + _externalDocument = PdfReader.Open(stream); + } + + /// + /// Creates an XPdfForm from a file. + /// + public static new XPdfForm FromFile(string path) + { + // TODO: Same file should return same object (that's why the function is static). + return new XPdfForm(path); + } + + /// + /// Creates an XPdfForm from a stream. + /// + public static new XPdfForm FromStream(Stream stream) + { + return new XPdfForm(stream); + } + + /* + void Initialize() + { + // ImageFormat has no overridden Equals... + } + */ + + /// + /// Sets the form in the state FormState.Finished. + /// + internal override void Finish() + { + if (_formState == FormState.NotATemplate || _formState == FormState.Finished) + return; + + base.Finish(); + + //if (Gfx.metafile != null) + // image = Gfx.metafile; + + //Debug.Assert(_fromState == FormState.Created || _fromState == FormState.UnderConstruction); + //_fromState = FormState.Finished; + //Gfx.Dispose(); + //Gfx = null; + + //if (_pdfRenderer != null) + //{ + // _pdfForm.Stream = new PdfDictionary.PdfStream(PdfEncoders.RawEncoding.GetBytes(pdfRenderer.GetContent()), this.pdfForm); + + // if (_document.Options.CompressContentStreams) + // { + // _pdfForm.Stream.Value = Filtering.FlateDecode.Encode(pdfForm.Stream.Value); + // _pdfForm.Elements["/Filter"] = new PdfName("/FlateDecode"); + // } + // int length = _pdfForm.Stream.Length; + // _pdfForm.Elements.SetInteger("/Length", length); + //} + } + + /// + /// Frees the memory occupied by the underlying imported PDF document, even if other XPdfForm objects + /// refer to this document. A reuse of this object doesn't fail, because the underlying PDF document + /// is re-imported if necessary. + /// + // TODO: NYI: Dispose + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + _disposed = true; + try + { + if (disposing) + { + //... + } + if (_externalDocument != null) + PdfDocument.Tls.DetachDocument(_externalDocument.Handle); + //... + } + finally + { + base.Dispose(disposing); + } + } + } + bool _disposed; + + /// + /// Gets or sets an image that is used for drawing if the current XGraphics object cannot handle + /// PDF forms. A place holder is useful for showing a preview of a page on the display, because + /// PDFsharp cannot render native PDF objects. + /// + public XImage PlaceHolder + { + get { return _placeHolder; } + set { _placeHolder = value; } + } + XImage _placeHolder; + + /// + /// Gets the underlying PdfPage (if one exists). + /// + public PdfPage Page + { + get + { + if (IsTemplate) + return null; + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return page; + } + } + + /// + /// Gets the number of pages in the PDF form. + /// + public int PageCount + { + get + { + if (IsTemplate) + return 1; + if (_pageCount == -1) + _pageCount = ExternalDocument.Pages.Count; + return _pageCount; + } + } + int _pageCount = -1; + + /// + /// Gets the width in point of the page identified by the property PageNumber. + /// + [Obsolete("Use either PixelWidth or PointWidth. Temporarily obsolete because of rearrangements for WPF.")] + public override double Width + { + get + { + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return page.Width; + } + } + + /// + /// Gets the height in point of the page identified by the property PageNumber. + /// + [Obsolete("Use either PixelHeight or PointHeight. Temporarily obsolete because of rearrangements for WPF.")] + public override double Height + { + get + { + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return page.Height; + } + } + + /// + /// Gets the width in point of the page identified by the property PageNumber. + /// + public override double PointWidth + { + get + { + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return page.Width; + } + } + + /// + /// Gets the height in point of the page identified by the property PageNumber. + /// + public override double PointHeight + { + get + { + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return page.Height; + } + } + + /// + /// Gets the width in point of the page identified by the property PageNumber. + /// + public override int PixelWidth + { + get + { + //PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + //return (int)page.Width; + return DoubleUtil.DoubleToInt(PointWidth); + } + } + + /// + /// Gets the height in point of the page identified by the property PageNumber. + /// + public override int PixelHeight + { + get + { + //PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + //return (int)page.Height; + return DoubleUtil.DoubleToInt(PointHeight); + } + } + + /// + /// Get the size of the page identified by the property PageNumber. + /// + public override XSize Size + { + get + { + PdfPage page = ExternalDocument.Pages[_pageNumber - 1]; + return new XSize(page.Width, page.Height); + } + } + + /// + /// Gets or sets the transformation matrix. + /// + public override XMatrix Transform + { + get { return _transform; } + set + { + if (_transform != value) + { + // discard PdfFromXObject when Transform changed + _pdfForm = null; + _transform = value; + } + } + } + + /// + /// Gets or sets the page number in the external PDF document this object refers to. The page number + /// is one-based, i.e. it is in the range from 1 to PageCount. The default value is 1. + /// + public int PageNumber + { + get { return _pageNumber; } + set + { + if (IsTemplate) + throw new InvalidOperationException("The page number of an XPdfForm template cannot be modified."); + + if (_pageNumber != value) + { + _pageNumber = value; + // dispose PdfFromXObject when number has changed + _pdfForm = null; + } + } + } + int _pageNumber = 1; + + /// + /// Gets or sets the page index in the external PDF document this object refers to. The page index + /// is zero-based, i.e. it is in the range from 0 to PageCount - 1. The default value is 0. + /// + public int PageIndex + { + get { return PageNumber - 1; } + set { PageNumber = value + 1; } + } + + /// + /// Gets the underlying document from which pages are imported. + /// + internal PdfDocument ExternalDocument + { + // The problem is that you can ask an XPdfForm about the number of its pages before it was + // drawn the first time. At this moment the XPdfForm doesn't know the document where it will + // be later draw on one of its pages. To prevent the import of the same document more than + // once, all imported documents of a thread are cached. The cache is local to the current + // thread and not to the appdomain, because I won't get problems in a multi-thread environment + // that I don't understand. + get + { + if (IsTemplate) + throw new InvalidOperationException("This XPdfForm is a template and not an imported PDF page; therefore it has no external document."); + + if (_externalDocument == null) + _externalDocument = PdfDocument.Tls.GetDocument(_path); + return _externalDocument; + } + } + internal PdfDocument _externalDocument; + + /// + /// Extracts the page number if the path has the form 'MyFile.pdf#123' and returns + /// the actual path without the number sign and the following digits. + /// + public static string ExtractPageNumber(string path, out int pageNumber) + { + if (path == null) + throw new ArgumentNullException("path"); + + pageNumber = 0; + int length = path.Length; + if (length != 0) + { + length--; + if (char.IsDigit(path, length)) + { + while (char.IsDigit(path, length) && length >= 0) + length--; + if (length > 0 && path[length] == '#') + { + // Must have at least one dot left of colon to distinguish from e.g. '#123' + if (path.IndexOf('.') != -1) + { + pageNumber = int.Parse(path.Substring(length + 1)); + path = path.Substring(0, length); + } + } + } + } + return path; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XPen.cs b/src/PDFsharp/src/PdfSharp/Drawing/XPen.cs new file mode 100644 index 00000000..c7f680c4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XPen.cs @@ -0,0 +1,404 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Internal; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiPen = System.Drawing.Pen; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using WpfPen =System.Windows.Media.Pen; +using WpfBrush =System.Windows.Media.Brush; +#endif +#if UWP +#endif + +namespace PdfSharp.Drawing +{ + // TODO Free GDI objects (pens, brushes, ...) automatically without IDisposable. + /// + /// Defines an object used to draw lines and curves. + /// + public sealed class XPen + { + /// + /// Initializes a new instance of the class. + /// + public XPen(XColor color) + : this(color, 1, false) + { } + + /// + /// Initializes a new instance of the class. + /// + public XPen(XColor color, double width) + : this(color, width, false) + { } + + internal XPen(XColor color, double width, bool immutable) + { + _color = color; + _width = width; + _lineJoin = XLineJoin.Miter; + _lineCap = XLineCap.Flat; + _dashStyle = XDashStyle.Solid; + _dashOffset = 0f; + _immutable = immutable; + } + + /// + /// Initializes a new instance of the class. + /// + public XPen(XPen pen) + { + _color = pen._color; + _width = pen._width; + _lineJoin = pen._lineJoin; + _lineCap = pen._lineCap; + _dashStyle = pen._dashStyle; + _dashOffset = pen._dashOffset; + _dashPattern = pen._dashPattern; + if (_dashPattern != null) + _dashPattern = (double[])_dashPattern.Clone(); + } + + /// + /// Clones this instance. + /// + public XPen Clone() + { + return new XPen(this); + } + + /// + /// Gets or sets the color. + /// + public XColor Color + { + get { return _color; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _color != value; + _color = value; + } + } + internal XColor _color; + + /// + /// Gets or sets the width. + /// + public double Width + { + get { return _width; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _width != value; + _width = value; + } + } + internal double _width; + + /// + /// Gets or sets the line join. + /// + public XLineJoin LineJoin + { + get { return _lineJoin; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _lineJoin != value; + _lineJoin = value; + } + } + internal XLineJoin _lineJoin; + + /// + /// Gets or sets the line cap. + /// + public XLineCap LineCap + { + get { return _lineCap; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _lineCap != value; + _lineCap = value; + } + } + internal XLineCap _lineCap; + + /// + /// Gets or sets the miter limit. + /// + public double MiterLimit + { + get { return _miterLimit; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _miterLimit != value; + _miterLimit = value; + } + } + internal double _miterLimit; + + /// + /// Gets or sets the dash style. + /// + public XDashStyle DashStyle + { + get { return _dashStyle; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _dashStyle != value; + _dashStyle = value; + } + } + internal XDashStyle _dashStyle; + + /// + /// Gets or sets the dash offset. + /// + public double DashOffset + { + get { return _dashOffset; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _dirty = _dirty || _dashOffset != value; + _dashOffset = value; + } + } + internal double _dashOffset; + + /// + /// Gets or sets the dash pattern. + /// + public double[] DashPattern + { + get + { + if (_dashPattern == null) + _dashPattern = new double[0]; + return _dashPattern; + } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + + int length = value.Length; + //if (length == 0) + // throw new ArgumentException("Dash pattern array must not be empty."); + + for (int idx = 0; idx < length; idx++) + { + if (value[idx] <= 0) + throw new ArgumentException("Dash pattern value must greater than zero."); + } + + _dirty = true; + _dashStyle = XDashStyle.Custom; + _dashPattern = (double[])value.Clone(); + } + } + internal double[] _dashPattern; + + /// + /// Gets or sets a value indicating whether the pen enables overprint when used in a PDF document. + /// Experimental, takes effect only on CMYK color mode. + /// + public bool Overprint + { + get { return _overprint; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XPen")); + _overprint = value; + } + } + internal bool _overprint; + +#if GDI +#if UseGdiObjects + /// + /// Implicit conversion from Pen to XPen + /// + public static implicit operator XPen(Pen pen) + { + XPen xpen; + try + { + Lock.EnterGdiPlus(); + switch (pen.PenType) + { + case PenType.SolidColor: + xpen = new XPen(pen.Color, pen.Width); + xpen.LineJoin = (XLineJoin)pen.LineJoin; + xpen.DashStyle = (XDashStyle)pen.DashStyle; + xpen._miterLimit = pen.MiterLimit; + break; + + default: + throw new NotImplementedException("Pen type not supported by PDFsharp."); + } + // Bug fixed by drice2@ageone.de + if (pen.DashStyle == System.Drawing.Drawing2D.DashStyle.Custom) + { + int length = pen.DashPattern.Length; + double[] pattern = new double[length]; + for (int idx = 0; idx < length; idx++) + pattern[idx] = pen.DashPattern[idx]; + xpen.DashPattern = pattern; + xpen._dashOffset = pen.DashOffset; + } + } + finally { Lock.ExitGdiPlus(); } + return xpen; + } +#endif + + internal System.Drawing.Pen RealizeGdiPen() + { + if (_dirty) + { + if (_gdiPen == null) + _gdiPen = new System.Drawing.Pen(_color.ToGdiColor(), (float)_width); + else + { + _gdiPen.Color = _color.ToGdiColor(); + _gdiPen.Width = (float)_width; + } + LineCap lineCap = XConvert.ToLineCap(_lineCap); + _gdiPen.StartCap = lineCap; + _gdiPen.EndCap = lineCap; + _gdiPen.LineJoin = XConvert.ToLineJoin(_lineJoin); + _gdiPen.DashOffset = (float)_dashOffset; + if (_dashStyle == XDashStyle.Custom) + { + int len = _dashPattern == null ? 0 : _dashPattern.Length; + float[] pattern = new float[len]; + for (int idx = 0; idx < len; idx++) + pattern[idx] = (float)_dashPattern[idx]; + _gdiPen.DashPattern = pattern; + } + else + _gdiPen.DashStyle = (System.Drawing.Drawing2D.DashStyle)_dashStyle; + } + return _gdiPen; + } +#endif + +#if WPF + internal WpfPen RealizeWpfPen() + { +#if !SILVERLIGHT + if (_dirty || !_dirty) // TODOWPF: XPen is frozen by design, WPF Pen can change + { + //if (_wpfPen == null) + _wpfPen = new WpfPen(new SolidColorBrush(_color.ToWpfColor()), _width); + //else + //{ + // _wpfPen.Brush = new SolidColorBrush(_color.ToWpfColor()); + // _wpfPen.Thickness = _width; + //} + PenLineCap lineCap = XConvert.ToPenLineCap(_lineCap); + _wpfPen.StartLineCap = lineCap; + _wpfPen.EndLineCap = lineCap; + _wpfPen.LineJoin = XConvert.ToPenLineJoin(_lineJoin); + if (_dashStyle == XDashStyle.Custom) + { + // TODOWPF: does not work in all cases + _wpfPen.DashStyle = new System.Windows.Media.DashStyle(_dashPattern, _dashOffset); + } + else + { + switch (_dashStyle) + { + case XDashStyle.Solid: + _wpfPen.DashStyle = DashStyles.Solid; + break; + + case XDashStyle.Dash: + //_wpfPen.DashStyle = DashStyles.Dash; + _wpfPen.DashStyle = new System.Windows.Media.DashStyle(new double[] { 2, 2 }, 0); + break; + + case XDashStyle.Dot: + //_wpfPen.DashStyle = DashStyles.Dot; + _wpfPen.DashStyle = new System.Windows.Media.DashStyle(new double[] { 0, 2 }, 1.5); + break; + + case XDashStyle.DashDot: + //_wpfPen.DashStyle = DashStyles.DashDot; + _wpfPen.DashStyle = new System.Windows.Media.DashStyle(new double[] { 2, 2, 0, 2 }, 0); + break; + + case XDashStyle.DashDotDot: + //_wpfPen.DashStyle = DashStyles.DashDotDot; + _wpfPen.DashStyle = new System.Windows.Media.DashStyle(new double[] { 2, 2, 0, 2, 0, 2 }, 0); + break; + } + } + } +#else + _wpfPen = new System.Windows.Media.Pen(); + _wpfPen.Brush = new SolidColorBrush(_color.ToWpfColor()); + _wpfPen.Thickness = _width; +#endif + return _wpfPen; + } +#endif + + bool _dirty = true; + readonly bool _immutable; +#if GDI + GdiPen _gdiPen; +#endif +#if WPF + WpfPen _wpfPen; +#endif + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XPens.cs b/src/PDFsharp/src/PdfSharp/Drawing/XPens.cs new file mode 100644 index 00000000..f02aeb74 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XPens.cs @@ -0,0 +1,1595 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable UnusedMember.Global + +#define USE_CACHE_is_not_thread_safe + +namespace PdfSharp.Drawing +{ + /// + /// Pens for all the pre-defined colors. + /// + public static class XPens + { + /// Gets a pre-defined XPen object. + public static XPen AliceBlue + { +#if USE_CACHE + get { return _aliceBlue ?? (_aliceBlue = new XPen(XColors.AliceBlue, 1, true)); } +#else + get { return new XPen(XColors.AliceBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen AntiqueWhite + { +#if USE_CACHE + get { return _antiqueWhite ?? (_antiqueWhite = new XPen(XColors.AntiqueWhite, 1, true)); } +#else + get { return new XPen(XColors.AntiqueWhite, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Aqua + { +#if USE_CACHE + get { return _aqua ?? (_aqua = new XPen(XColors.Aqua, 1, true)); } +#else + get { return new XPen(XColors.Aqua, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Aquamarine + { +#if USE_CACHE + get { return _aquamarine ?? (_aquamarine = new XPen(XColors.Aquamarine, 1, true)); } +#else + get { return new XPen(XColors.Aquamarine, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Azure + { +#if USE_CACHE + get { return _azure ?? (_azure = new XPen(XColors.Azure, 1, true)); } +#else + get { return new XPen(XColors.Azure, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Beige + { +#if USE_CACHE + get { return _beige ?? (_beige = new XPen(XColors.Beige, 1, true)); } +#else + get { return new XPen(XColors.Beige, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Bisque + { +#if USE_CACHE + get { return _bisque ?? (_bisque = new XPen(XColors.Bisque, 1, true)); } +#else + get { return new XPen(XColors.Bisque, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Black + { +#if USE_CACHE + get { return _black ?? (_black = new XPen(XColors.Black, 1, true)); } +#else + get { return new XPen(XColors.Black, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen BlanchedAlmond + { +#if USE_CACHE + get { return _blanchedAlmond ?? (_blanchedAlmond = new XPen(XColors.BlanchedAlmond, 1, true)); } +#else + get { return new XPen(XColors.BlanchedAlmond, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Blue + { +#if USE_CACHE + get { return _blue ?? (_blue = new XPen(XColors.Blue, 1, true)); } +#else + get { return new XPen(XColors.Blue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen BlueViolet + { +#if USE_CACHE + get { return _blueViolet ?? (_blueViolet = new XPen(XColors.BlueViolet, 1, true)); } +#else + get { return new XPen(XColors.BlueViolet, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Brown + { +#if USE_CACHE + get { return _brown ?? (_brown = new XPen(XColors.Brown, 1, true)); } +#else + get { return new XPen(XColors.Brown, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen BurlyWood + { +#if USE_CACHE + get { return _burlyWood ?? (_burlyWood = new XPen(XColors.BurlyWood, 1, true)); } +#else + get { return new XPen(XColors.BurlyWood, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen CadetBlue + { +#if USE_CACHE + get { return _cadetBlue ?? (_cadetBlue = new XPen(XColors.CadetBlue, 1, true)); } +#else + get { return new XPen(XColors.CadetBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Chartreuse + { +#if USE_CACHE + get { return _chartreuse ?? (_chartreuse = new XPen(XColors.Chartreuse, 1, true)); } +#else + get { return new XPen(XColors.Chartreuse, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Chocolate + { +#if USE_CACHE + get { return _chocolate ?? (_chocolate = new XPen(XColors.Chocolate, 1, true)); } +#else + get { return new XPen(XColors.Chocolate, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Coral + { +#if USE_CACHE + get { return _coral ?? (_coral = new XPen(XColors.Coral, 1, true)); } +#else + get { return new XPen(XColors.Coral, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen CornflowerBlue + { +#if USE_CACHE + get { return _cornflowerBlue ?? (_cornflowerBlue = new XPen(XColors.CornflowerBlue, 1, true)); } +#else + get { return new XPen(XColors.CornflowerBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Cornsilk + { +#if USE_CACHE + get { return _cornsilk ?? (_cornsilk = new XPen(XColors.Cornsilk, 1, true)); } +#else + get { return new XPen(XColors.Cornsilk, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Crimson + { +#if USE_CACHE + get { return _crimson ?? (_crimson = new XPen(XColors.Crimson, 1, true)); } +#else + get { return new XPen(XColors.Crimson, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Cyan + { +#if USE_CACHE + get { return _cyan ?? (_cyan = new XPen(XColors.Cyan, 1, true)); } +#else + get { return new XPen(XColors.Cyan, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkBlue + { +#if USE_CACHE + get { return _darkBlue ?? (_darkBlue = new XPen(XColors.DarkBlue, 1, true)); } +#else + get { return new XPen(XColors.DarkBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkCyan + { +#if USE_CACHE + get { return _darkCyan ?? (_darkCyan = new XPen(XColors.DarkCyan, 1, true)); } +#else + get { return new XPen(XColors.DarkCyan, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkGoldenrod + { +#if USE_CACHE + get { return _darkGoldenrod ?? (_darkGoldenrod = new XPen(XColors.DarkGoldenrod, 1, true)); } +#else + get { return new XPen(XColors.DarkGoldenrod, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkGray + { +#if USE_CACHE + get { return _darkGray ?? (_darkGray = new XPen(XColors.DarkGray, 1, true)); } +#else + get { return new XPen(XColors.DarkGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkGreen + { +#if USE_CACHE + get { return _darkGreen ?? (_darkGreen = new XPen(XColors.DarkGreen, 1, true)); } +#else + get { return new XPen(XColors.DarkGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkKhaki + { +#if USE_CACHE + get { return _darkKhaki ?? (_darkKhaki = new XPen(XColors.DarkKhaki, 1, true)); } +#else + get { return new XPen(XColors.DarkKhaki, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkMagenta + { +#if USE_CACHE + get { return _darkMagenta ?? (_darkMagenta = new XPen(XColors.DarkMagenta, 1, true)); } +#else + get { return new XPen(XColors.DarkMagenta, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkOliveGreen + { +#if USE_CACHE + get { return _darkOliveGreen ?? (_darkOliveGreen = new XPen(XColors.DarkOliveGreen, 1, true)); } +#else + get { return new XPen(XColors.DarkOliveGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkOrange + { +#if USE_CACHE + get { return _darkOrange ?? (_darkOrange = new XPen(XColors.DarkOrange, 1, true)); } +#else + get { return new XPen(XColors.DarkOrange, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkOrchid + { +#if USE_CACHE + get { return _darkOrchid ?? (_darkOrchid = new XPen(XColors.DarkOrchid, 1, true)); } +#else + get { return new XPen(XColors.DarkOrchid, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkRed + { +#if USE_CACHE + get { return _darkRed ?? (_darkRed = new XPen(XColors.DarkRed, 1, true)); } +#else + get { return new XPen(XColors.DarkRed, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkSalmon + { +#if USE_CACHE + get { return _darkSalmon ?? (_darkSalmon = new XPen(XColors.DarkSalmon, 1, true)); } +#else + get { return new XPen(XColors.DarkSalmon, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkSeaGreen + { +#if USE_CACHE + get { return _darkSeaGreen ?? (_darkSeaGreen = new XPen(XColors.DarkSeaGreen, 1, true)); } +#else + get { return new XPen(XColors.DarkSeaGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkSlateBlue + { +#if USE_CACHE + get { return _darkSlateBlue ?? (_darkSlateBlue = new XPen(XColors.DarkSlateBlue, 1, true)); } +#else + get { return new XPen(XColors.DarkSlateBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkSlateGray + { +#if USE_CACHE + get { return _darkSlateGray ?? (_darkSlateGray = new XPen(XColors.DarkSlateGray, 1, true)); } +#else + get { return new XPen(XColors.DarkSlateGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkTurquoise + { +#if USE_CACHE + get { return _darkTurquoise ?? (_darkTurquoise = new XPen(XColors.DarkTurquoise, 1, true)); } +#else + get { return new XPen(XColors.DarkTurquoise, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DarkViolet + { +#if USE_CACHE + get { return _darkViolet ?? (_darkViolet = new XPen(XColors.DarkViolet, 1, true)); } +#else + get { return new XPen(XColors.DarkViolet, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DeepPink + { +#if USE_CACHE + get { return _deepPink ?? (_deepPink = new XPen(XColors.DeepPink, 1, true)); } +#else + get { return new XPen(XColors.DeepPink, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DeepSkyBlue + { +#if USE_CACHE + get { return _deepSkyBlue ?? (_deepSkyBlue = new XPen(XColors.DeepSkyBlue, 1, true)); } +#else + get { return new XPen(XColors.DeepSkyBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DimGray + { +#if USE_CACHE + get { return _dimGray ?? (_dimGray = new XPen(XColors.DimGray, 1, true)); } +#else + get { return new XPen(XColors.DimGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen DodgerBlue + { +#if USE_CACHE + get { return _dodgerBlue ?? (_dodgerBlue = new XPen(XColors.DodgerBlue, 1, true)); } +#else + get { return new XPen(XColors.DodgerBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Firebrick + { +#if USE_CACHE + get { return _firebrick ?? (_firebrick = new XPen(XColors.Firebrick, 1, true)); } +#else + get { return new XPen(XColors.Firebrick, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen FloralWhite + { +#if USE_CACHE + get { return _floralWhite ?? (_floralWhite = new XPen(XColors.FloralWhite, 1, true)); } +#else + get { return new XPen(XColors.FloralWhite, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen ForestGreen + { +#if USE_CACHE + get { return _forestGreen ?? (_forestGreen = new XPen(XColors.ForestGreen, 1, true)); } +#else + get { return new XPen(XColors.ForestGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Fuchsia + { +#if USE_CACHE + get { return _fuchsia ?? (_fuchsia = new XPen(XColors.Fuchsia, 1, true)); } +#else + get { return new XPen(XColors.Fuchsia, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Gainsboro + { +#if USE_CACHE + get { return _gainsboro ?? (_gainsboro = new XPen(XColors.Gainsboro, 1, true)); } +#else + get { return new XPen(XColors.Gainsboro, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen GhostWhite + { +#if USE_CACHE + get { return _ghostWhite ?? (_ghostWhite = new XPen(XColors.GhostWhite, 1, true)); } +#else + get { return new XPen(XColors.GhostWhite, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Gold + { +#if USE_CACHE + get { return _gold ?? (_gold = new XPen(XColors.Gold, 1, true)); } +#else + get { return new XPen(XColors.Gold, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Goldenrod + { +#if USE_CACHE + get { return _goldenrod ?? (_goldenrod = new XPen(XColors.Goldenrod, 1, true)); } +#else + get { return new XPen(XColors.Goldenrod, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Gray + { +#if USE_CACHE + get { return _gray ?? (_gray = new XPen(XColors.Gray, 1, true)); } +#else + get { return new XPen(XColors.Gray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Green + { +#if USE_CACHE + get { return _green ?? (_green = new XPen(XColors.Green, 1, true)); } +#else + get { return new XPen(XColors.Green, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen GreenYellow + { +#if USE_CACHE + get { return _greenYellow ?? (_greenYellow = new XPen(XColors.GreenYellow, 1, true)); } +#else + get { return new XPen(XColors.GreenYellow, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Honeydew + { +#if USE_CACHE + get { return _honeydew ?? (_honeydew = new XPen(XColors.Honeydew, 1, true)); } +#else + get { return new XPen(XColors.Honeydew, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen HotPink + { +#if USE_CACHE + get { return _hotPink ?? (_hotPink = new XPen(XColors.HotPink, 1, true)); } +#else + get { return new XPen(XColors.HotPink, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen IndianRed + { +#if USE_CACHE + get { return _indianRed ?? (_indianRed = new XPen(XColors.IndianRed, 1, true)); } +#else + get { return new XPen(XColors.IndianRed, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Indigo + { +#if USE_CACHE + get { return _indigo ?? (_indigo = new XPen(XColors.Indigo, 1, true)); } +#else + get { return new XPen(XColors.Indigo, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Ivory + { +#if USE_CACHE + get { return _ivory ?? (_ivory = new XPen(XColors.Ivory, 1, true)); } +#else + get { return new XPen(XColors.Ivory, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Khaki + { +#if USE_CACHE + get { return _khaki ?? (_khaki = new XPen(XColors.Khaki, 1, true)); } +#else + get { return new XPen(XColors.Khaki, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Lavender + { +#if USE_CACHE + get { return _lavender ?? (_lavender = new XPen(XColors.Lavender, 1, true)); } +#else + get { return new XPen(XColors.Lavender, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LavenderBlush + { +#if USE_CACHE + get { return _lavenderBlush ?? (_lavenderBlush = new XPen(XColors.LavenderBlush, 1, true)); } +#else + get { return new XPen(XColors.LavenderBlush, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LawnGreen + { +#if USE_CACHE + get { return _lawnGreen ?? (_lawnGreen = new XPen(XColors.LawnGreen, 1, true)); } +#else + get { return new XPen(XColors.LawnGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LemonChiffon + { +#if USE_CACHE + get { return _lemonChiffon ?? (_lemonChiffon = new XPen(XColors.LemonChiffon, 1, true)); } +#else + get { return new XPen(XColors.LemonChiffon, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightBlue + { +#if USE_CACHE + get { return _lightBlue ?? (_lightBlue = new XPen(XColors.LightBlue, 1, true)); } +#else + get { return new XPen(XColors.LightBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightCoral + { +#if USE_CACHE + get { return _lightCoral ?? (_lightCoral = new XPen(XColors.LightCoral, 1, true)); } +#else + get { return new XPen(XColors.LightCoral, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightCyan + { +#if USE_CACHE + get { return _lightCyan ?? (_lightCyan = new XPen(XColors.LightCyan, 1, true)); } +#else + get { return new XPen(XColors.LightCyan, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightGoldenrodYellow + { +#if USE_CACHE + get { return _lightGoldenrodYellow ?? (_lightGoldenrodYellow = new XPen(XColors.LightGoldenrodYellow, 1, true)); } +#else + get { return new XPen(XColors.LightGoldenrodYellow, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightGray + { +#if USE_CACHE + get { return _lightGray ?? (_lightGray = new XPen(XColors.LightGray, 1, true)); } +#else + get { return new XPen(XColors.LightGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightGreen + { +#if USE_CACHE + get { return _lightGreen ?? (_lightGreen = new XPen(XColors.LightGreen, 1, true)); } +#else + get { return new XPen(XColors.LightGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightPink + { +#if USE_CACHE + get { return _lightPink ?? (_lightPink = new XPen(XColors.LightPink, 1, true)); } +#else + get { return new XPen(XColors.LightPink, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightSalmon + { +#if USE_CACHE + get { return _lightSalmon ?? (_lightSalmon = new XPen(XColors.LightSalmon, 1, true)); } +#else + get { return new XPen(XColors.LightSalmon, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightSeaGreen + { +#if USE_CACHE + get { return _lightSeaGreen ?? (_lightSeaGreen = new XPen(XColors.LightSeaGreen, 1, true)); } +#else + get { return new XPen(XColors.LightSeaGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightSkyBlue + { +#if USE_CACHE + get { return _lightSkyBlue ?? (_lightSkyBlue = new XPen(XColors.LightSkyBlue, 1, true)); } +#else + get { return new XPen(XColors.LightSkyBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightSlateGray + { +#if USE_CACHE + get { return _lightSlateGray ?? (_lightSlateGray = new XPen(XColors.LightSlateGray, 1, true)); } +#else + get { return new XPen(XColors.LightSlateGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightSteelBlue + { +#if USE_CACHE + get { return _lightSteelBlue ?? (_lightSteelBlue = new XPen(XColors.LightSteelBlue, 1, true)); } +#else + get { return new XPen(XColors.LightSteelBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LightYellow + { +#if USE_CACHE + get { return _lightYellow ?? (_lightYellow = new XPen(XColors.LightYellow, 1, true)); } +#else + get { return new XPen(XColors.LightYellow, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Lime + { +#if USE_CACHE + get { return _lime ?? (_lime = new XPen(XColors.Lime, 1, true)); } +#else + get { return new XPen(XColors.Lime, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen LimeGreen + { +#if USE_CACHE + get { return _limeGreen ?? (_limeGreen = new XPen(XColors.LimeGreen, 1, true)); } +#else + get { return new XPen(XColors.LimeGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Linen + { +#if USE_CACHE + get { return _linen ?? (_linen = new XPen(XColors.Linen, 1, true)); } +#else + get { return new XPen(XColors.Linen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Magenta + { +#if USE_CACHE + get { return _magenta ?? (_magenta = new XPen(XColors.Magenta, 1, true)); } +#else + get { return new XPen(XColors.Magenta, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Maroon + { +#if USE_CACHE + get { return _maroon ?? (_maroon = new XPen(XColors.Maroon, 1, true)); } +#else + get { return new XPen(XColors.Maroon, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumAquamarine + { +#if USE_CACHE + get { return _mediumAquamarine ?? (_mediumAquamarine = new XPen(XColors.MediumAquamarine, 1, true)); } +#else + get { return new XPen(XColors.MediumAquamarine, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumBlue + { +#if USE_CACHE + get { return _mediumBlue ?? (_mediumBlue = new XPen(XColors.MediumBlue, 1, true)); } +#else + get { return new XPen(XColors.MediumBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumOrchid + { +#if USE_CACHE + get { return _mediumOrchid ?? (_mediumOrchid = new XPen(XColors.MediumOrchid, 1, true)); } +#else + get { return new XPen(XColors.MediumOrchid, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumPurple + { +#if USE_CACHE + get { return _mediumPurple ?? (_mediumPurple = new XPen(XColors.MediumPurple, 1, true)); } +#else + get { return new XPen(XColors.MediumPurple, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumSeaGreen + { +#if USE_CACHE + get { return _mediumSeaGreen ?? (_mediumSeaGreen = new XPen(XColors.MediumSeaGreen, 1, true)); } +#else + get { return new XPen(XColors.MediumSeaGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumSlateBlue + { +#if USE_CACHE + get { return _mediumSlateBlue ?? (_mediumSlateBlue = new XPen(XColors.MediumSlateBlue, 1, true)); } +#else + get { return new XPen(XColors.MediumSlateBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumSpringGreen + { +#if USE_CACHE + get { return _mediumSpringGreen ?? (_mediumSpringGreen = new XPen(XColors.MediumSpringGreen, 1, true)); } +#else + get { return new XPen(XColors.MediumSpringGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumTurquoise + { +#if USE_CACHE + get { return _mediumTurquoise ?? (_mediumTurquoise = new XPen(XColors.MediumTurquoise, 1, true)); } +#else + get { return new XPen(XColors.MediumTurquoise, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MediumVioletRed + { +#if USE_CACHE + get { return _mediumVioletRed ?? (_mediumVioletRed = new XPen(XColors.MediumVioletRed, 1, true)); } +#else + get { return new XPen(XColors.MediumVioletRed, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MidnightBlue + { +#if USE_CACHE + get { return _midnightBlue ?? (_midnightBlue = new XPen(XColors.MidnightBlue, 1, true)); } +#else + get { return new XPen(XColors.MidnightBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MintCream + { +#if USE_CACHE + get { return _mintCream ?? (_mintCream = new XPen(XColors.MintCream, 1, true)); } +#else + get { return new XPen(XColors.MintCream, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen MistyRose + { +#if USE_CACHE + get { return _mistyRose ?? (_mistyRose = new XPen(XColors.MistyRose, 1, true)); } +#else + get { return new XPen(XColors.MistyRose, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Moccasin + { +#if USE_CACHE + get { return _moccasin ?? (_moccasin = new XPen(XColors.Moccasin, 1, true)); } +#else + get { return new XPen(XColors.Moccasin, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen NavajoWhite + { +#if USE_CACHE + get { return _navajoWhite ?? (_navajoWhite = new XPen(XColors.NavajoWhite, 1, true)); } +#else + get { return new XPen(XColors.NavajoWhite, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Navy + { +#if USE_CACHE + get { return _navy ?? (_navy = new XPen(XColors.Navy, 1, true)); } +#else + get { return new XPen(XColors.Navy, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen OldLace + { +#if USE_CACHE + get { return _oldLace ?? (_oldLace = new XPen(XColors.OldLace, 1, true)); } +#else + get { return new XPen(XColors.OldLace, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Olive + { +#if USE_CACHE + get { return _olive ?? (_olive = new XPen(XColors.Olive, 1, true)); } +#else + get { return new XPen(XColors.Olive, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen OliveDrab + { +#if USE_CACHE + get { return _oliveDrab ?? (_oliveDrab = new XPen(XColors.OliveDrab, 1, true)); } +#else + get { return new XPen(XColors.OliveDrab, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Orange + { +#if USE_CACHE + get { return _orange ?? (_orange = new XPen(XColors.Orange, 1, true)); } +#else + get { return new XPen(XColors.Orange, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen OrangeRed + { +#if USE_CACHE + get { return _orangeRed ?? (_orangeRed = new XPen(XColors.OrangeRed, 1, true)); } +#else + get { return new XPen(XColors.OrangeRed, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Orchid + { +#if USE_CACHE + get { return _orchid ?? (_orchid = new XPen(XColors.Orchid, 1, true)); } +#else + get { return new XPen(XColors.Orchid, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PaleGoldenrod + { +#if USE_CACHE + get { return _paleGoldenrod ?? (_paleGoldenrod = new XPen(XColors.PaleGoldenrod, 1, true)); } +#else + get { return new XPen(XColors.PaleGoldenrod, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PaleGreen + { +#if USE_CACHE + get { return _paleGreen ?? (_paleGreen = new XPen(XColors.PaleGreen, 1, true)); } +#else + get { return new XPen(XColors.PaleGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PaleTurquoise + { +#if USE_CACHE + get { return _paleTurquoise ?? (_paleTurquoise = new XPen(XColors.PaleTurquoise, 1, true)); } +#else + get { return new XPen(XColors.PaleTurquoise, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PaleVioletRed + { +#if USE_CACHE + get { return _paleVioletRed ?? (_paleVioletRed = new XPen(XColors.PaleVioletRed, 1, true)); } +#else + get { return new XPen(XColors.PaleVioletRed, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PapayaWhip + { +#if USE_CACHE + get { return _papayaWhip ?? (_papayaWhip = new XPen(XColors.PapayaWhip, 1, true)); } +#else + get { return new XPen(XColors.PapayaWhip, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PeachPuff + { +#if USE_CACHE + get { return _peachPuff ?? (_peachPuff = new XPen(XColors.PeachPuff, 1, true)); } +#else + get { return new XPen(XColors.PeachPuff, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Peru + { +#if USE_CACHE + get { return _peru ?? (_peru = new XPen(XColors.Peru, 1, true)); } +#else + get { return new XPen(XColors.Peru, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Pink + { +#if USE_CACHE + get { return _pink ?? (_pink = new XPen(XColors.Pink, 1, true)); } +#else + get { return new XPen(XColors.Pink, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Plum + { +#if USE_CACHE + get { return _plum ?? (_plum = new XPen(XColors.Plum, 1, true)); } +#else + get { return new XPen(XColors.Plum, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen PowderBlue + { +#if USE_CACHE + get { return _powderBlue ?? (_powderBlue = new XPen(XColors.PowderBlue, 1, true)); } +#else + get { return new XPen(XColors.PowderBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Purple + { +#if USE_CACHE + get { return _purple ?? (_purple = new XPen(XColors.Purple, 1, true)); } +#else + get { return new XPen(XColors.Purple, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Red + { +#if USE_CACHE + get { return _red ?? (_red = new XPen(XColors.Red, 1, true)); } +#else + get { return new XPen(XColors.Red, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen RosyBrown + { +#if USE_CACHE + get { return _rosyBrown ?? (_rosyBrown = new XPen(XColors.RosyBrown, 1, true)); } +#else + get { return new XPen(XColors.RosyBrown, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen RoyalBlue + { +#if USE_CACHE + get { return _royalBlue ?? (_royalBlue = new XPen(XColors.RoyalBlue, 1, true)); } +#else + get { return new XPen(XColors.RoyalBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SaddleBrown + { +#if USE_CACHE + get { return _saddleBrown ?? (_saddleBrown = new XPen(XColors.SaddleBrown, 1, true)); } +#else + get { return new XPen(XColors.SaddleBrown, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Salmon + { +#if USE_CACHE + get { return _salmon ?? (_salmon = new XPen(XColors.Salmon, 1, true)); } +#else + get { return new XPen(XColors.Salmon, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SandyBrown + { +#if USE_CACHE + get { return _sandyBrown ?? (_sandyBrown = new XPen(XColors.SandyBrown, 1, true)); } +#else + get { return new XPen(XColors.SandyBrown, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SeaGreen + { +#if USE_CACHE + get { return _seaGreen ?? (_seaGreen = new XPen(XColors.SeaGreen, 1, true)); } +#else + get { return new XPen(XColors.SeaGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SeaShell + { +#if USE_CACHE + get { return _seaShell ?? (_seaShell = new XPen(XColors.SeaShell, 1, true)); } +#else + get { return new XPen(XColors.SeaShell, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Sienna + { +#if USE_CACHE + get { return _sienna ?? (_sienna = new XPen(XColors.Sienna, 1, true)); } +#else + get { return new XPen(XColors.Sienna, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Silver + { +#if USE_CACHE + get { return _silver ?? (_silver = new XPen(XColors.Silver, 1, true)); } +#else + get { return new XPen(XColors.Silver, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SkyBlue + { +#if USE_CACHE + get { return _skyBlue ?? (_skyBlue = new XPen(XColors.SkyBlue, 1, true)); } +#else + get { return new XPen(XColors.SkyBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SlateBlue + { +#if USE_CACHE + get { return _slateBlue ?? (_slateBlue = new XPen(XColors.SlateBlue, 1, true)); } +#else + get { return new XPen(XColors.SlateBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SlateGray + { +#if USE_CACHE + get { return _slateGray ?? (_slateGray = new XPen(XColors.SlateGray, 1, true)); } +#else + get { return new XPen(XColors.SlateGray, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Snow + { +#if USE_CACHE + get { return _snow ?? (_snow = new XPen(XColors.Snow, 1, true)); } +#else + get { return new XPen(XColors.Snow, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SpringGreen + { +#if USE_CACHE + get { return _springGreen ?? (_springGreen = new XPen(XColors.SpringGreen, 1, true)); } +#else + get { return new XPen(XColors.SpringGreen, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen SteelBlue + { +#if USE_CACHE + get { return _steelBlue ?? (_steelBlue = new XPen(XColors.SteelBlue, 1, true)); } +#else + get { return new XPen(XColors.SteelBlue, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Tan + { +#if USE_CACHE + get { return _tan ?? (_tan = new XPen(XColors.Tan, 1, true)); } +#else + get { return new XPen(XColors.Tan, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Teal + { +#if USE_CACHE + get { return _teal ?? (_teal = new XPen(XColors.Teal, 1, true)); } +#else + get { return new XPen(XColors.Teal, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Thistle + { +#if USE_CACHE + get { return _thistle ?? (_thistle = new XPen(XColors.Thistle, 1, true)); } +#else + get { return new XPen(XColors.Thistle, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Tomato + { +#if USE_CACHE + get { return _tomato ?? (_tomato = new XPen(XColors.Tomato, 1, true)); } +#else + get { return new XPen(XColors.Tomato, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Transparent + { +#if USE_CACHE + get { return _transparent ?? (_transparent = new XPen(XColors.Transparent, 1, true)); } +#else + get { return new XPen(XColors.Transparent, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Turquoise + { +#if USE_CACHE + get { return _turquoise ?? (_turquoise = new XPen(XColors.Turquoise, 1, true)); } +#else + get { return new XPen(XColors.Turquoise, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Violet + { +#if USE_CACHE + get { return _violet ?? (_violet = new XPen(XColors.Violet, 1, true)); } +#else + get { return new XPen(XColors.Violet, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Wheat + { +#if USE_CACHE + get { return _wheat ?? (_wheat = new XPen(XColors.Wheat, 1, true)); } +#else + get { return new XPen(XColors.Wheat, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen White + { +#if USE_CACHE + get { return _white ?? (_white = new XPen(XColors.White, 1, true)); } +#else + get { return new XPen(XColors.White, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen WhiteSmoke + { +#if USE_CACHE + get { return _whiteSmoke ?? (_whiteSmoke = new XPen(XColors.WhiteSmoke, 1, true)); } +#else + get { return new XPen(XColors.WhiteSmoke, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen Yellow + { +#if USE_CACHE + get { return _yellow ?? (_yellow = new XPen(XColors.Yellow, 1, true)); } +#else + get { return new XPen(XColors.Yellow, 1, true); } +#endif + } + + /// Gets a pre-defined XPen object. + public static XPen YellowGreen + { +#if USE_CACHE + get { return _yellowGreen ?? (_yellowGreen = new XPen(XColors.YellowGreen, 1, true)); } +#else + get { return new XPen(XColors.YellowGreen, 1, true); } +#endif + } + +#if USE_CACHE + static XPen _aliceBlue; + static XPen _antiqueWhite; + static XPen _aqua; + static XPen _aquamarine; + static XPen _azure; + static XPen _beige; + static XPen _bisque; + static XPen _black; + static XPen _blanchedAlmond; + static XPen _blue; + static XPen _blueViolet; + static XPen _brown; + static XPen _burlyWood; + static XPen _cadetBlue; + static XPen _chartreuse; + static XPen _chocolate; + static XPen _coral; + static XPen _cornflowerBlue; + static XPen _cornsilk; + static XPen _crimson; + static XPen _cyan; + static XPen _darkBlue; + static XPen _darkCyan; + static XPen _darkGoldenrod; + static XPen _darkGray; + static XPen _darkGreen; + static XPen _darkKhaki; + static XPen _darkMagenta; + static XPen _darkOliveGreen; + static XPen _darkOrange; + static XPen _darkOrchid; + static XPen _darkRed; + static XPen _darkSalmon; + static XPen _darkSeaGreen; + static XPen _darkSlateBlue; + static XPen _darkSlateGray; + static XPen _darkTurquoise; + static XPen _darkViolet; + static XPen _deepPink; + static XPen _deepSkyBlue; + static XPen _dimGray; + static XPen _dodgerBlue; + static XPen _firebrick; + static XPen _floralWhite; + static XPen _forestGreen; + static XPen _fuchsia; + static XPen _gainsboro; + static XPen _ghostWhite; + static XPen _gold; + static XPen _goldenrod; + static XPen _gray; + static XPen _green; + static XPen _greenYellow; + static XPen _honeydew; + static XPen _hotPink; + static XPen _indianRed; + static XPen _indigo; + static XPen _ivory; + static XPen _khaki; + static XPen _lavender; + static XPen _lavenderBlush; + static XPen _lawnGreen; + static XPen _lemonChiffon; + static XPen _lightBlue; + static XPen _lightCoral; + static XPen _lightCyan; + static XPen _lightGoldenrodYellow; + static XPen _lightGray; + static XPen _lightGreen; + static XPen _lightPink; + static XPen _lightSalmon; + static XPen _lightSeaGreen; + static XPen _lightSkyBlue; + static XPen _lightSlateGray; + static XPen _lightSteelBlue; + static XPen _lightYellow; + static XPen _lime; + static XPen _limeGreen; + static XPen _linen; + static XPen _magenta; + static XPen _maroon; + static XPen _mediumAquamarine; + static XPen _mediumBlue; + static XPen _mediumOrchid; + static XPen _mediumPurple; + static XPen _mediumSeaGreen; + static XPen _mediumSlateBlue; + static XPen _mediumSpringGreen; + static XPen _mediumTurquoise; + static XPen _mediumVioletRed; + static XPen _midnightBlue; + static XPen _mintCream; + static XPen _mistyRose; + static XPen _moccasin; + static XPen _navajoWhite; + static XPen _navy; + static XPen _oldLace; + static XPen _olive; + static XPen _oliveDrab; + static XPen _orange; + static XPen _orangeRed; + static XPen _orchid; + static XPen _paleGoldenrod; + static XPen _paleGreen; + static XPen _paleTurquoise; + static XPen _paleVioletRed; + static XPen _papayaWhip; + static XPen _peachPuff; + static XPen _peru; + static XPen _pink; + static XPen _plum; + static XPen _powderBlue; + static XPen _purple; + static XPen _red; + static XPen _rosyBrown; + static XPen _royalBlue; + static XPen _saddleBrown; + static XPen _salmon; + static XPen _sandyBrown; + static XPen _seaGreen; + static XPen _seaShell; + static XPen _sienna; + static XPen _silver; + static XPen _skyBlue; + static XPen _slateBlue; + static XPen _slateGray; + static XPen _snow; + static XPen _springGreen; + static XPen _steelBlue; + static XPen _tan; + static XPen _teal; + static XPen _thistle; + static XPen _tomato; + static XPen _transparent; + static XPen _turquoise; + static XPen _violet; + static XPen _wheat; + static XPen _white; + static XPen _whiteSmoke; + static XPen _yellow; + static XPen _yellowGreen; +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XPoint.cs b/src/PDFsharp/src/PdfSharp/Drawing/XPoint.cs new file mode 100644 index 00000000..bf479b0d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XPoint.cs @@ -0,0 +1,434 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +#if CORE +#endif +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +#endif +#if !EDF_CORE +using PdfSharp.Internal; +#else +using PdfSharp.Internal; +#endif + +#if !EDF_CORE +namespace PdfSharp.Drawing +#else +namespace Edf.Drawing +#endif +{ + /// + /// Represents a pair of floating point x- and y-coordinates that defines a point + /// in a two-dimensional plane. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + [Serializable] + [StructLayout(LayoutKind.Sequential)] // TypeConverter(typeof(PointConverter)), ValueSerializer(typeof(PointValueSerializer))] + public struct XPoint : IFormattable + { + /// + /// Initializes a new instance of the XPoint class with the specified coordinates. + /// + public XPoint(double x, double y) + { + _x = x; + _y = y; + } + +#if GDI + /// + /// Initializes a new instance of the XPoint class with the specified point. + /// + public XPoint(System.Drawing.Point point) + { + _x = point.X; + _y = point.Y; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Initializes a new instance of the XPoint class with the specified point. + /// + public XPoint(SysPoint point) + { + _x = point.X; + _y = point.Y; + } +#endif + +#if GDI + /// + /// Initializes a new instance of the XPoint class with the specified point. + /// + public XPoint(PointF point) + { + _x = point.X; + _y = point.Y; + } +#endif + + /// + /// Determines whether two points are equal. + /// + public static bool operator ==(XPoint point1, XPoint point2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return point1._x == point2._x && point1._y == point2._y; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Determines whether two points are not equal. + /// + public static bool operator !=(XPoint point1, XPoint point2) + { + return !(point1 == point2); + } + + /// + /// Indicates whether the specified points are equal. + /// + public static bool Equals(XPoint point1, XPoint point2) + { + return point1.X.Equals(point2.X) && point1.Y.Equals(point2.Y); + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + public override bool Equals(object o) + { + if (!(o is XPoint)) + return false; + return Equals(this, (XPoint)o); + } + + /// + /// Indicates whether this instance and a specified point are equal. + /// + public bool Equals(XPoint value) + { + return Equals(this, value); + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode(); + } + + /// + /// Parses the point from a string. + /// + public static XPoint Parse(string source) + { + CultureInfo cultureInfo = CultureInfo.InvariantCulture; + TokenizerHelper helper = new TokenizerHelper(source, cultureInfo); + string str = helper.NextTokenRequired(); + XPoint point = new XPoint(Convert.ToDouble(str, cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo)); + helper.LastTokenRequired(); + return point; + } + + /// + /// Parses an array of points from a string. + /// + public static XPoint[] ParsePoints(string value) + { + if (value == null) + throw new ArgumentNullException("value"); + // TODO: Reflect reliabel implementation from Avalon + // TODOWPF + string[] values = value.Split(' '); + int count = values.Length; + XPoint[] points = new XPoint[count]; + for (int idx = 0; idx < count; idx++) + points[idx] = Parse(values[idx]); + return points; + } + + /// + /// Gets the x-coordinate of this XPoint. + /// + public double X + { + get { return _x; } + set { _x = value; } + } + double _x; + + /// + /// Gets the x-coordinate of this XPoint. + /// + public double Y + { + get { return _y; } + set { _y = value; } + } + double _y; + +#if CORE +#if UseGdiObjects + /// + /// Converts this XPoint to a System.Drawing.Point. + /// + public PointF ToPointF() + { + return new PointF((float)_x, (float)_y); + } +#endif +#endif + +#if GDI + /// + /// Converts this XPoint to a System.Drawing.Point. + /// + public PointF ToPointF() + { + return new PointF((float)_x, (float)_y); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Converts this XPoint to a System.Windows.Point. + /// + public SysPoint ToPoint() + { + return new SysPoint(_x, _y); + } +#endif + + /// + /// Converts this XPoint to a human readable string. + /// + public override string ToString() + { + return ConvertToString(null, null); + } + + /// + /// Converts this XPoint to a human readable string. + /// + public string ToString(IFormatProvider provider) + { + return ConvertToString(null, provider); + } + + /// + /// Converts this XPoint to a human readable string. + /// + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + /// + /// Implements ToString. + /// + internal string ConvertToString(string format, IFormatProvider provider) + { + char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider); + provider = provider ?? CultureInfo.InvariantCulture; + return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}", new object[] { numericListSeparator, _x, _y }); + } + + /// + /// Offsets the x and y value of this point. + /// + public void Offset(double offsetX, double offsetY) + { + _x += offsetX; + _y += offsetY; + } + + /// + /// Adds a point and a vector. + /// + public static XPoint operator +(XPoint point, XVector vector) + { + return new XPoint(point._x + vector.X, point._y + vector.Y); + } + + /// + /// Adds a point and a size. + /// + public static XPoint operator +(XPoint point, XSize size) // TODO: make obsolete + { + return new XPoint(point._x + size.Width, point._y + size.Height); + } + + /// + /// Adds a point and a vector. + /// + public static XPoint Add(XPoint point, XVector vector) + { + return new XPoint(point._x + vector.X, point._y + vector.Y); + } + + /// + /// Subtracts a vector from a point. + /// + public static XPoint operator -(XPoint point, XVector vector) + { + return new XPoint(point._x - vector.X, point._y - vector.Y); + } + + /// + /// Subtracts a vector from a point. + /// + public static XPoint Subtract(XPoint point, XVector vector) + { + return new XPoint(point._x - vector.X, point._y - vector.Y); + } + + /// + /// Subtracts a point from a point. + /// + public static XVector operator -(XPoint point1, XPoint point2) + { + return new XVector(point1._x - point2._x, point1._y - point2._y); + } + + /// + /// Subtracts a size from a point. + /// + [Obsolete("Use XVector instead of XSize as second parameter.")] + public static XPoint operator -(XPoint point, XSize size) // TODO: make obsolete + { + return new XPoint(point._x - size.Width, point._y - size.Height); + } + + /// + /// Subtracts a point from a point. + /// + public static XVector Subtract(XPoint point1, XPoint point2) + { + return new XVector(point1._x - point2._x, point1._y - point2._y); + } + + /// + /// Multiplies a point with a matrix. + /// + public static XPoint operator *(XPoint point, XMatrix matrix) + { + return matrix.Transform(point); + } + + /// + /// Multiplies a point with a matrix. + /// + public static XPoint Multiply(XPoint point, XMatrix matrix) + { + return matrix.Transform(point); + } + + /// + /// Multiplies a point with a scalar value. + /// + public static XPoint operator *(XPoint point, double value) + { + return new XPoint(point._x * value, point._y * value); + } + + /// + /// Multiplies a point with a scalar value. + /// + public static XPoint operator *(double value, XPoint point) + { + return new XPoint(value * point._x, value * point._y); + } + + /// + /// Performs an explicit conversion from XPoint to XSize. + /// + public static explicit operator XSize(XPoint point) + { + return new XSize(Math.Abs(point._x), Math.Abs(point._y)); + } + + /// + /// Performs an explicit conversion from XPoint to XVector. + /// + public static explicit operator XVector(XPoint point) + { + return new XVector(point._x, point._y); + } + +#if WPF || NETFX_CORE + /// + /// Performs an implicit conversion from XPoint to Point. + /// + public static implicit operator SysPoint(XPoint point) + { + return new SysPoint(point.X, point.Y); + } + + /// + /// Performs an implicit conversion from Point to XPoint. + /// + public static implicit operator XPoint(SysPoint point) + { + return new XPoint(point.X, point.Y); + } +#endif + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return String.Format(CultureInfo.InvariantCulture, "point=({0:" + format + "}, {1:" + format + "})", _x, _y); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XPrivateFontCollection.cs b/src/PDFsharp/src/PdfSharp/Drawing/XPrivateFontCollection.cs new file mode 100644 index 00000000..ef31dce9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XPrivateFontCollection.cs @@ -0,0 +1,441 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using PdfSharp.Fonts; +#if CORE || GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +using GdiPrivateFontCollection = System.Drawing.Text.PrivateFontCollection; +#endif +#if WPF +using System.Windows.Markup; +using WpfFonts = System.Windows.Media.Fonts; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif + +namespace PdfSharp.Drawing +{ +#if true + /// + /// Makes fonts that are not installed on the system available within the current application domain.
+ /// In Silverlight required for all fonts used in PDF documents. + ///
+ public sealed class XPrivateFontCollection + { + // This one is global and can only grow. It is not possible to remove fonts that have been added. + + /// + /// Initializes a new instance of the class. + /// + XPrivateFontCollection() + { + // HACK: Use one global PrivateFontCollection in GDI+ + } + +#if GDI + //internal PrivateFontCollection PrivateFontCollection + //{ + // get { return privateFontCollection; } + // set { privateFontCollection = value; } + //} + + GdiPrivateFontCollection GetPrivateFontCollection() + { + // Create only if really needed. + if (_privateFontCollection == null) + _privateFontCollection = new GdiPrivateFontCollection(); + return _privateFontCollection; + } + + // PrivateFontCollection of GDI+ + private GdiPrivateFontCollection _privateFontCollection; +#endif + + /// + /// Gets the global font collection. + /// + internal static XPrivateFontCollection Singleton + { + get { return _singleton; } + } + internal static XPrivateFontCollection _singleton = new XPrivateFontCollection(); + +#if GDI + /// + /// Adds the font data to the font collections. + /// + [Obsolete("Use Add(Stream stream)")] + public void AddFont(byte[] data, string familyName) + { + if (String.IsNullOrEmpty(familyName)) + throw new ArgumentNullException("familyName"); + + //if (glyphTypeface == null) + // throw new ArgumentNullException("glyphTypeface"); + + // Add to GDI+ PrivateFontCollection + int length = data.Length; + + // Copy data without unsafe code + IntPtr ip = Marshal.AllocCoTaskMem(length); + Marshal.Copy(data, 0, ip, length); + GetPrivateFontCollection().AddMemoryFont(ip, length); + // Do not free the memory here, AddMemoryFont stores a pointer, not a copy! + //Marshal.FreeCoTaskMem(ip); + //privateFonts.Add(glyphTypeface); + } +#endif + + /// + /// Adds the specified font data to the global PrivateFontCollection. + /// Family name and style are automatically retrieved from the font. + /// +#if GDI + [Obsolete("Use Add(Stream stream)")] +#else + [Obsolete("Use the GDI build of PDFsharp and use Add(Stream stream)")] +#endif + public static void AddFont(string filename) + { + throw new NotImplementedException(); + //XGlyphTypeface glyphTypeface = new XGlyphTypeface(filename); + //Global.AddGlyphTypeface(glyphTypeface); + } + +#if GDI + /// + /// Adds the specified font data to the global PrivateFontCollection. + /// Family name and style are automatically retrieved from the font. + /// + [Obsolete("Use Add(stream).")] + public static void AddFont(Stream stream) + { + Add(stream); + } + + /// + /// Adds the specified font data to the global PrivateFontCollection. + /// Family name and style are automatically retrieved from the font. + /// + public static void Add(Stream stream) + { + int length = (int)stream.Length; + byte[] bytes = new byte[length]; + stream.Read(bytes, 0, length); + Add(bytes); + } + + /// + /// Adds the specified font data to the global PrivateFontCollection. + /// Family name and style are automatically retrieved from the font. + /// + public static void Add(byte[] font) + { + IntPtr unmanagedPointer = Marshal.AllocCoTaskMem(font.Length); + Marshal.Copy(font, 0, unmanagedPointer, font.Length); + Singleton.GetPrivateFontCollection().AddMemoryFont(unmanagedPointer, font.Length); + // Do not free the memory here, AddMemoryFont stores a pointer, not a copy! + //Marshal.FreeCoTaskMem(ip); + + XFontSource fontSource = XFontSource.GetOrCreateFrom(font); + + string familyName = fontSource.FontName; + + if (familyName.EndsWith(" Regular", StringComparison.OrdinalIgnoreCase)) + familyName = familyName.Substring(0, familyName.Length - 8); + + bool bold = fontSource.Fontface.os2.IsBold; + bool italic = fontSource.Fontface.os2.IsItalic; + IncompetentlyMakeAHackToFixAProblemYouWoldNeverHaveIfYouUseAFontResolver(fontSource, ref familyName, ref bold, ref italic); + string key = MakeKey(familyName, bold, italic); + Singleton._fontSources.Add(key, fontSource); + + string typefaceKey = XGlyphTypeface.ComputeKey(familyName, bold, italic); + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + + static void IncompetentlyMakeAHackToFixAProblemYouWoldNeverHaveIfYouUseAFontResolver(XFontSource fontSource, + ref string familyName, ref bool bold, ref bool italic) + { + const string regularSuffix = " Regular"; + const string boldSuffix = " Bold"; + const string italicSuffix = " Italic"; + const string boldItalicSuffix = " Bold Italic"; + const string italicBoldSuffix = " Italic Bold"; + + if (familyName.EndsWith(regularSuffix, StringComparison.OrdinalIgnoreCase)) + { + familyName = familyName.Substring(0, familyName.Length - regularSuffix.Length); + Debug.Assert(!bold && !italic); + bold = italic = false; + } + else if (familyName.EndsWith(boldItalicSuffix, StringComparison.OrdinalIgnoreCase) || familyName.EndsWith(italicBoldSuffix, StringComparison.OrdinalIgnoreCase)) + { + familyName = familyName.Substring(0, familyName.Length - boldItalicSuffix.Length); + Debug.Assert(bold && italic); + bold = italic = true; + } + else if (familyName.EndsWith(boldSuffix, StringComparison.OrdinalIgnoreCase)) + { + familyName = familyName.Substring(0, familyName.Length - boldSuffix.Length); + Debug.Assert(bold && !italic); + bold = true; + italic = false; + } + else if (familyName.EndsWith(italicSuffix, StringComparison.OrdinalIgnoreCase)) + { + familyName = familyName.Substring(0, familyName.Length - italicSuffix.Length); + Debug.Assert(!bold && italic); + bold = false; + italic = true; + } + else + { + Debug.Assert(!bold && !italic); + bold = false; + italic = false; + } + } +#endif + + /// + /// Adds the specified font data to the global PrivateFontCollection. + /// Family name and style are automatically retrieved from the font. + /// +#if GDI + [Obsolete("Use Add(Stream stream)")] +#else + [Obsolete("Use the GDI build of PDFsharp and use Add(Stream stream)")] +#endif + public static void AddFont(Stream stream, string facename) + { + throw new NotImplementedException(); + //XGlyphTypeface glyphTypeface = new XGlyphTypeface(stream, facename); + //Global.AddGlyphTypeface(glyphTypeface); + } + + // /// + // /// Adds XGlyphTypeface to internal collection. + // /// Family name and style are automatically retrieved from the font. + // /// + // void AddGlyphTypeface(XGlyphTypeface glyphTypeface) + // { + // string name = MakeName(glyphTypeface); + // if (_typefaces.ContainsKey(name)) + // throw new InvalidOperationException(PSSR.FontAlreadyAdded(glyphTypeface.DisplayName)); + + // _typefaces.Add(name, glyphTypeface); + // //Debug.WriteLine("Font added: " + name); + + //#if GDI + // // Add to GDI+ PrivateFontCollection singleton. + // byte[] data = glyphTypeface.Fontface.FontSource.Bytes; + // int length = data.Length; + + // IntPtr ip = Marshal.AllocCoTaskMem(length); + // Marshal.Copy(data, 0, ip, length); + // _privateFontCollection.AddMemoryFont(ip, length); + // // Do not free the memory here, AddMemoryFont stores a pointer, not a copy! + // // Marshal.FreeCoTaskMem(ip); + //#endif + + //#if WPF + //#endif + // } + +#if WPF + /// + /// Initializes a new instance of the FontFamily class from the specified font family name and an optional base uniform resource identifier (URI) value. + /// Sample: Add(new Uri("pack://application:,,,/"), "./myFonts/#FontFamilyName");) + /// + /// Specifies the base URI that is used to resolve familyName. + /// The family name or names that comprise the new FontFamily. Multiple family names should be separated by commas. + public static void Add(Uri baseUri, string familyName) + { + Uri uri = new Uri("pack://application:,,,/"); + + // TODO: What means 'Multiple family names should be separated by commas.'? + // does not work + + + if (String.IsNullOrEmpty(familyName)) + throw new ArgumentNullException("familyName"); + + if (familyName.Contains(",")) + throw new NotImplementedException("Only one family name is supported."); + + // Family name starts right of '#'. + int idxHash = familyName.IndexOf('#'); + if (idxHash < 0) + throw new ArgumentException("Family name must contain a '#'. Example './#MyFontFamilyName'", "familyName"); + + string key = familyName.Substring(idxHash + 1); + if (String.IsNullOrEmpty(key)) + throw new ArgumentException("familyName has invalid format."); + + if (Singleton._fontFamilies.ContainsKey(key)) + throw new ArgumentException("An entry with the specified family name already exists."); + +#if !SILVERLIGHT +#if DEBUG_ + foreach (WpfFontFamily fontFamily1 in WpfFonts.GetFontFamilies(baseUri, familyName)) + { + ICollection wpfTypefaces = fontFamily1.GetTypefaces(); + wpfTypefaces.GetType(); + } +#endif + // Create WPF font family. + WpfFontFamily fontFamily = new WpfFontFamily(baseUri, familyName); + //System.Windows.Media.FontFamily x; + // Required for new Uri("pack://application:,,,/") + // ReSharper disable once ObjectCreationAsStatement + // new System.Windows.Application(); + +#else + System.Windows.Media.FontFamily fontFamily = new System.Windows.Media.FontFamily(familyName); +#endif + + // Check whether font data really exists +#if DEBUG && !SILVERLIGHT + ICollection list = fontFamily.GetTypefaces(); + foreach (WpfTypeface typeFace in list) + { + Debug.WriteLine(String.Format("{0}, {1}, {2}, {3}, {4}", familyName, typeFace.FaceNames[FontHelper.XmlLanguageEnUs], typeFace.Style, typeFace.Weight, typeFace.Stretch)); + WpfGlyphTypeface glyphTypeface; + if (!typeFace.TryGetGlyphTypeface(out glyphTypeface)) + { + Debug.WriteLine(" Glyph typeface does not exists."); + //throw new ArgumentException("Font with the specified family name does not exist."); + } + } +#endif + + Singleton._fontFamilies.Add(key, fontFamily); + } +#endif + + //internal static XGlyphTypeface TryGetXGlyphTypeface(string familyName, XFontStyle style) + //{ + // string name = MakeName(familyName, style); + + // XGlyphTypeface typeface; + // _global._typefaces.TryGetValue(name, out typeface); + // return typeface; + //} + +#if GDI + internal static GdiFont TryCreateFont(string name, double size, GdiFontStyle style, out XFontSource fontSource) + { + fontSource = null; + try + { + GdiPrivateFontCollection pfc = Singleton._privateFontCollection; + if (pfc == null) + return null; +#if true + string key = MakeKey(name, (XFontStyle)style); + if (Singleton._fontSources.TryGetValue(key, out fontSource)) + { + GdiFont font = new GdiFont(name, (float)size, style, GraphicsUnit.World); +#if DEBUG_ + Debug.Assert(StringComparer.OrdinalIgnoreCase.Compare(name, font.Name) == 0); + Debug.Assert(font.Bold == ((style & GdiFontStyle.Bold) != 0)); + Debug.Assert(font.Italic == ((style & GdiFontStyle.Italic) != 0)); +#endif + return font; + } + return null; +#else + foreach (GdiFontFamily family in pfc.Families) + { + if (string.Compare(family.Name, name, StringComparison.OrdinalIgnoreCase) == 0) + { + GdiFont font = new GdiFont(family, (float)size, style, GraphicsUnit.World); + if (string.Compare(font.Name, name, StringComparison.OrdinalIgnoreCase) != 0) + { + // Style simulation is not implemented in GDI+. + // Use WPF build. + } + return font; + } + } +#endif + } + catch (Exception ex) + { + // Ignore exception and return null. + Debug.WriteLine(ex.ToString()); + } + return null; + } +#endif + +#if WPF && !SILVERLIGHT + internal static WpfTypeface TryCreateTypeface(string name, XFontStyle style, out WpfFontFamily fontFamily) + { + if (Singleton._fontFamilies.TryGetValue(name, out fontFamily)) + { + WpfTypeface typeface = FontHelper.CreateTypeface(fontFamily, style); + return typeface; + } + return null; + } +#endif + + static string MakeKey(string familyName, XFontStyle style) + { + return MakeKey(familyName, (style & XFontStyle.Bold) != 0, (style & XFontStyle.Italic) != 0); + } + + static string MakeKey(string familyName, bool bold, bool italic) + { + return familyName + "#" + (bold ? "b" : "") + (italic ? "i" : ""); + } + + readonly Dictionary _typefaces = new Dictionary(); +#if GDI + //List privateFonts = new List(); + readonly Dictionary _fontSources = new Dictionary(StringComparer.OrdinalIgnoreCase); +#endif +#if WPF + readonly Dictionary _fontFamilies = new Dictionary(StringComparer.OrdinalIgnoreCase); +#endif + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XRadialGradientBrush.cs b/src/PDFsharp/src/PdfSharp/Drawing/XRadialGradientBrush.cs new file mode 100644 index 00000000..d1db57d2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XRadialGradientBrush.cs @@ -0,0 +1,324 @@ +#region PDFsharp - A .NET library for processing PDF + +// +// Authors: +// Stefan Lange (mailto:Stefan.Lange@pdfsharp.com) +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#endregion PDFsharp - A .NET library for processing PDF + +using System; +using System.ComponentModel; +using PdfSharp.Internal; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiPoint = System.Drawing.Point; +using GdiLinearGradientBrush = System.Drawing.Drawing2D.LinearGradientBrush; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +using WpfBrush = System.Windows.Media.Brush; +using WpfRadialGradientBrush = System.Windows.Media.RadialGradientBrush; +#endif +#if UWP +using Windows.UI; +using Windows.UI.Xaml.Media; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +#endif + +// ReSharper disable RedundantNameQualifier because it is required for hybrid build + +namespace PdfSharp.Drawing +{ + /// + /// Defines a Brush with a radial gradient. + /// + public sealed class XRadialGradientBrush : XGradientBrush + { +#if GDI +/// +/// Initializes a new instance of the class. +/// + public XRadialGradientBrush(GdiPoint point1, GdiPoint point2, double innerRadius, double outerRadius, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), innerRadius, outerRadius, color1, color2) + { } + + /// + /// Initializes a new instance of the class. + /// + public XRadialGradientBrush(PointF point1, PointF point2, double innerRadius, double outerRadius, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), innerRadius, outerRadius, color1, color2) + { } +#endif + +#if WPF + /// + /// Initializes a new instance of the class. + /// + public XRadialGradientBrush(System.Windows.Point point1, System.Windows.Point point2, double innerRadius, + double outerRadius, XColor color1, XColor color2) + : this(new XPoint(point1), new XPoint(point2), innerRadius, outerRadius, color1, color2) + { + } +#endif + + /// + /// Initializes a new instance of the class. + /// + public XRadialGradientBrush(XPoint point1, XPoint point2, double innerRadius, double outerRadius, XColor color1, + XColor color2) + { + _point1 = point1; + _point2 = point2; + _innerRadius = innerRadius; + _outerRadius = outerRadius; + _color1 = color1; + _color2 = color2; + } + + /// + /// Gets or sets an XMatrix that defines a local geometric transform for this RadialGradientBrush. + /// + public XMatrix Transform + { + get { return _matrix; } + set { _matrix = value; } + } + + /// + /// Gets or sets the inner radius. + /// + public double InnerRadius + { + get { return _innerRadius; } + set { _innerRadius = value; } + } + + /// + /// Gets or sets the outer radius. + /// + public double OuterRadius + { + get { return _outerRadius; } + set { _outerRadius = value; } + } + + /// + /// Translates the brush with the specified offset. + /// + public void TranslateTransform(double dx, double dy) + { + _matrix.TranslatePrepend(dx, dy); + } + + /// + /// Translates the brush with the specified offset. + /// + public void TranslateTransform(double dx, double dy, XMatrixOrder order) + { + _matrix.Translate(dx, dy, order); + } + + /// + /// Scales the brush with the specified scalars. + /// + public void ScaleTransform(double sx, double sy) + { + _matrix.ScalePrepend(sx, sy); + } + + /// + /// Scales the brush with the specified scalars. + /// + public void ScaleTransform(double sx, double sy, XMatrixOrder order) + { + _matrix.Scale(sx, sy, order); + } + + /// + /// Rotates the brush with the specified angle. + /// + public void RotateTransform(double angle) + { + _matrix.RotatePrepend(angle); + } + + /// + /// Rotates the brush with the specified angle. + /// + public void RotateTransform(double angle, XMatrixOrder order) + { + _matrix.Rotate(angle, order); + } + + /// + /// Multiply the brush transformation matrix with the specified matrix. + /// + public void MultiplyTransform(XMatrix matrix) + { + _matrix.Prepend(matrix); + } + + /// + /// Multiply the brush transformation matrix with the specified matrix. + /// + public void MultiplyTransform(XMatrix matrix, XMatrixOrder order) + { + _matrix.Multiply(matrix, order); + } + + /// + /// Resets the brush transformation matrix with identity matrix. + /// + public void ResetTransform() + { + _matrix = new XMatrix(); //XMatrix.Identity; + } + + //public void SetBlendTriangularShape(double focus); + //public void SetBlendTriangularShape(double focus, double scale); + //public void SetSigmaBellShape(double focus); + //public void SetSigmaBellShape(double focus, double scale); + +#if GDI +//TODO: Change from LinearGradient to RadialGradient + internal override System.Drawing.Brush RealizeGdiBrush() + { + //if (dirty) + //{ + // if (brush == null) + // brush = new SolidBrush(color.ToGdiColor()); + // else + // { + // brush.Color = color.ToGdiColor(); + // } + // dirty = false; + //} + +#if not_implemented + // TODO: use dirty to optimize code + GdiLinearGradientBrush brush; + try + { + Lock.EnterGdiPlus(); + if (_useRect) + { + brush = new GdiLinearGradientBrush(_rect.ToRectangleF(), + _color1.ToGdiColor(), _color2.ToGdiColor(), (LinearGradientMode)_linearGradientMode); + } + else + { + brush = new GdiLinearGradientBrush( + _point1.ToPointF(), _point2.ToPointF(), + _color1.ToGdiColor(), _color2.ToGdiColor()); + } + if (!_matrix.IsIdentity) + brush.Transform = _matrix.ToGdiMatrix(); + //brush.WrapMode = WrapMode.Clamp; + } + finally { Lock.ExitGdiPlus(); } + return brush; +#else + return null; +#endif + } +#endif + +#if WPF + internal override WpfBrush RealizeWpfBrush() + { + //if (this.dirty) + //{ + // if (this.brush == null) + // this.brush = new SolidBrush(this.color.ToGdiColor()); + // else + // { + // this.brush.Color = this.color.ToGdiColor(); + // } + // this.dirty = false; + //} + + WpfRadialGradientBrush brush; +#if !SILVERLIGHT + brush = new WpfRadialGradientBrush(_color1.ToWpfColor(), _color2.ToWpfColor()); + brush.RadiusX = OuterRadius; + brush.RadiusY = OuterRadius; + //brush = new System.Drawing.Drawing2D.LinearGradientBrush( + // this.point1.ToPointF(), this.point2.ToPointF(), + // this.color1.ToGdiColor(), this.color2.ToGdiColor()); +#else + GradientStop gs1 = new GradientStop(); + gs1.Color = _color1.ToWpfColor(); + gs1.Offset = 0; + + GradientStop gs2 = new GradientStop(); + gs2.Color = _color2.ToWpfColor(); + gs2.Offset = 1; + + GradientStopCollection gsc = new GradientStopCollection(); + gsc.Add(gs1); + gsc.Add(gs2); + + brush = new WpfRadialGradientBrush(_color1.ToWpfColor(), _color2.ToWpfColor()); + brush.RadiusX = OuterRadius; + brush.RadiusY = OuterRadius; +#endif + if (!_matrix.IsIdentity) + { +#if !SILVERLIGHT + brush.Transform = new MatrixTransform(_matrix.ToWpfMatrix()); +#else + MatrixTransform transform = new MatrixTransform(); + transform.Matrix = _matrix.ToWpfMatrix(); + brush.Transform = transform; +#endif + } + return brush; //this.brush; + } +#endif + +#if UWP + internal override ICanvasBrush RealizeCanvasBrush() + { + ICanvasBrush brush; + + brush = new CanvasSolidColorBrush(CanvasDevice.GetSharedDevice(), Colors.RoyalBlue); + + return brush; + } +#endif + + internal XPoint _point1, _point2; + internal XColor _color1, _color2; + internal double _innerRadius, _outerRadius; + internal XMatrix _matrix; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XRect.cs b/src/PDFsharp/src/PdfSharp/Drawing/XRect.cs new file mode 100644 index 00000000..0bb4aa7f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XRect.cs @@ -0,0 +1,847 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +#if CORE +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +using SysRect = System.Windows.Rect; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +using SysRect = Windows.Foundation.Rect; +#endif +#if !EDF_CORE +using PdfSharp.Internal; +#else +using PdfSharp.Internal; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Stores a set of four floating-point numbers that represent the location and size of a rectangle. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + [Serializable, StructLayout(LayoutKind.Sequential)] // , ValueSerializer(typeof(RectValueSerializer)), TypeConverter(typeof(RectConverter))] + public struct XRect : IFormattable + { + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(double x, double y, double width, double height) + { + if (width < 0 || height < 0) + throw new ArgumentException("WidthAndHeightCannotBeNegative"); //SR.Get(SRID.Size_WidthAndHeightCannotBeNegative, new object[0])); + _x = x; + _y = y; + _width = width; + _height = height; + } + + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(XPoint point1, XPoint point2) + { + _x = Math.Min(point1.X, point2.X); + _y = Math.Min(point1.Y, point2.Y); + _width = Math.Max(Math.Max(point1.X, point2.X) - _x, 0); + _height = Math.Max(Math.Max(point1.Y, point2.Y) - _y, 0); + } + + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(XPoint point, XVector vector) + : this(point, point + vector) + { } + + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(XPoint location, XSize size) + { + if (size.IsEmpty) + this = s_empty; + else + { + _x = location.X; + _y = location.Y; + _width = size.Width; + _height = size.Height; + } + } + + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(XSize size) + { + if (size.IsEmpty) + this = s_empty; + else + { + _x = _y = 0; + _width = size.Width; + _height = size.Height; + } + } + +#if GDI + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(PointF location, SizeF size) + { + _x = location.X; + _y = location.Y; + _width = size.Width; + _height = size.Height; + } +#endif + +#if GDI + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(RectangleF rect) + { + _x = rect.X; + _y = rect.Y; + _width = rect.Width; + _height = rect.Height; + } +#endif + +#if WPF || NETFX_CORE + /// + /// Initializes a new instance of the XRect class. + /// + public XRect(SysRect rect) + { + _x = rect.X; + _y = rect.Y; + _width = rect.Width; + _height = rect.Height; + } +#endif + + /// + /// Creates a rectangle from for straight lines. + /// + // ReSharper disable InconsistentNaming + public static XRect FromLTRB(double left, double top, double right, double bottom) + // ReSharper restore InconsistentNaming + { + return new XRect(left, top, right - left, bottom - top); + } + + /// + /// Determines whether the two rectangles are equal. + /// + public static bool operator ==(XRect rect1, XRect rect2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return rect1.X == rect2.X && rect1.Y == rect2.Y && rect1.Width == rect2.Width && rect1.Height == rect2.Height; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Determines whether the two rectangles are not equal. + /// + public static bool operator !=(XRect rect1, XRect rect2) + { + return !(rect1 == rect2); + } + + /// + /// Determines whether the two rectangles are equal. + /// + public static bool Equals(XRect rect1, XRect rect2) + { + if (rect1.IsEmpty) + return rect2.IsEmpty; + return rect1.X.Equals(rect2.X) && rect1.Y.Equals(rect2.Y) && rect1.Width.Equals(rect2.Width) && rect1.Height.Equals(rect2.Height); + } + + /// + /// Determines whether this instance and the specified object are equal. + /// + public override bool Equals(object o) + { + if (!(o is XRect)) + return false; + return Equals(this, (XRect)o); + } + + /// + /// Determines whether this instance and the specified rect are equal. + /// + public bool Equals(XRect value) + { + return Equals(this, value); + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + if (IsEmpty) + return 0; + return X.GetHashCode() ^ Y.GetHashCode() ^ Width.GetHashCode() ^ Height.GetHashCode(); + } + + /// + /// Parses the rectangle from a string. + /// + public static XRect Parse(string source) + { + XRect empty; + CultureInfo cultureInfo = CultureInfo.InvariantCulture; + TokenizerHelper helper = new TokenizerHelper(source, cultureInfo); + string str = helper.NextTokenRequired(); + if (str == "Empty") + empty = Empty; + else + empty = new XRect(Convert.ToDouble(str, cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo)); + helper.LastTokenRequired(); + return empty; + } + + /// + /// Converts this XRect to a human readable string. + /// + public override string ToString() + { + return ConvertToString(null, null); + } + + /// + /// Converts this XRect to a human readable string. + /// + public string ToString(IFormatProvider provider) + { + return ConvertToString(null, provider); + } + + /// + /// Converts this XRect to a human readable string. + /// + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + if (IsEmpty) + return "Empty"; + char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider); + provider = provider ?? CultureInfo.InvariantCulture; + // ReSharper disable FormatStringProblem + return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}", new object[] { numericListSeparator, _x, _y, _width, _height }); + // ReSharper restore FormatStringProblem + } + + /// + /// Gets the empty rectangle. + /// + public static XRect Empty + { + get { return s_empty; } + } + + /// + /// Gets a value indicating whether this instance is empty. + /// + public bool IsEmpty + { + get { return _width < 0; } + } + + /// + /// Gets or sets the location of the rectangle. + /// + public XPoint Location + { + get { return new XPoint(_x, _y); } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + _x = value.X; + _y = value.Y; + } + } + + /// + /// Gets or sets the size of the rectangle. + /// + //[Browsable(false)] + public XSize Size + { + get + { + if (IsEmpty) + return XSize.Empty; + return new XSize(_width, _height); + } + set + { + if (value.IsEmpty) + this = s_empty; + else + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + _width = value.Width; + _height = value.Height; + } + } + } + + /// + /// Gets or sets the X value of the rectangle. + /// + public double X + { + get { return _x; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + _x = value; + } + } + double _x; + + /// + /// Gets or sets the Y value of the rectangle. + /// + public double Y + { + get { return _y; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + _y = value; + } + } + double _y; + + /// + /// Gets or sets the width of the rectangle. + /// + public double Width + { + get { return _width; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + if (value < 0) + throw new ArgumentException("WidthCannotBeNegative"); //SR.Get(SRID.Size_WidthCannotBeNegative, new object[0])); + + _width = value; + } + } + double _width; + + /// + /// Gets or sets the height of the rectangle. + /// + public double Height + { + get { return _height; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptyRect"); //SR.Get(SRID.Rect_CannotModifyEmptyRect, new object[0])); + if (value < 0) + throw new ArgumentException("HeightCannotBeNegative"); //SR.Get(SRID.Size_HeightCannotBeNegative, new object[0])); + _height = value; + } + } + double _height; + + /// + /// Gets the x-axis value of the left side of the rectangle. + /// + public double Left + { + get { return _x; } + } + + /// + /// Gets the y-axis value of the top side of the rectangle. + /// + public double Top + { + get { return _y; } + } + + /// + /// Gets the x-axis value of the right side of the rectangle. + /// + public double Right + { + get + { + if (IsEmpty) + return double.NegativeInfinity; + return _x + _width; + } + } + + /// + /// Gets the y-axis value of the bottom side of the rectangle. + /// + public double Bottom + { + get + { + if (IsEmpty) + return double.NegativeInfinity; + return _y + _height; + } + } + + /// + /// Gets the position of the top-left corner of the rectangle. + /// + public XPoint TopLeft + { + get { return new XPoint(Left, Top); } + } + + /// + /// Gets the position of the top-right corner of the rectangle. + /// + public XPoint TopRight + { + get { return new XPoint(Right, Top); } + } + + /// + /// Gets the position of the bottom-left corner of the rectangle. + /// + public XPoint BottomLeft + { + get { return new XPoint(Left, Bottom); } + } + + /// + /// Gets the position of the bottom-right corner of the rectangle. + /// + public XPoint BottomRight + { + get { return new XPoint(Right, Bottom); } + } + + /// + /// Gets the center of the rectangle. + /// + //[Browsable(false)] + public XPoint Center + { + get { return new XPoint(_x + _width / 2, _y + _height / 2); } + } + + /// + /// Indicates whether the rectangle contains the specified point. + /// + public bool Contains(XPoint point) + { + return Contains(point.X, point.Y); + } + + /// + /// Indicates whether the rectangle contains the specified point. + /// + public bool Contains(double x, double y) + { + if (IsEmpty) + return false; + return ContainsInternal(x, y); + } + + /// + /// Indicates whether the rectangle contains the specified rectangle. + /// + public bool Contains(XRect rect) + { + return !IsEmpty && !rect.IsEmpty && + _x <= rect._x && _y <= rect._y && + _x + _width >= rect._x + rect._width && _y + _height >= rect._y + rect._height; + } + + /// + /// Indicates whether the specified rectangle intersects with the current rectangle. + /// + public bool IntersectsWith(XRect rect) + { + return !IsEmpty && !rect.IsEmpty && + rect.Left <= Right && rect.Right >= Left && + rect.Top <= Bottom && rect.Bottom >= Top; + } + + /// + /// Sets current rectangle to the intersection of the current rectangle and the specified rectangle. + /// + public void Intersect(XRect rect) + { + if (!IntersectsWith(rect)) + this = Empty; + else + { + double left = Math.Max(Left, rect.Left); + double top = Math.Max(Top, rect.Top); + _width = Math.Max(Math.Min(Right, rect.Right) - left, 0.0); + _height = Math.Max(Math.Min(Bottom, rect.Bottom) - top, 0.0); + _x = left; + _y = top; + } + } + + /// + /// Returns the intersection of two rectangles. + /// + public static XRect Intersect(XRect rect1, XRect rect2) + { + rect1.Intersect(rect2); + return rect1; + } + + /// + /// Sets current rectangle to the union of the current rectangle and the specified rectangle. + /// + public void Union(XRect rect) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (IsEmpty) + this = rect; + else if (!rect.IsEmpty) + { + double left = Math.Min(Left, rect.Left); + double top = Math.Min(Top, rect.Top); + if (rect.Width == Double.PositiveInfinity || Width == Double.PositiveInfinity) + _width = Double.PositiveInfinity; + else + { + double right = Math.Max(Right, rect.Right); + _width = Math.Max(right - left, 0.0); + } + + if (rect.Height == Double.PositiveInfinity || _height == Double.PositiveInfinity) + _height = Double.PositiveInfinity; + else + { + double bottom = Math.Max(Bottom, rect.Bottom); + _height = Math.Max(bottom - top, 0.0); + } + _x = left; + _y = top; + } + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Returns the union of two rectangles. + /// + public static XRect Union(XRect rect1, XRect rect2) + { + rect1.Union(rect2); + return rect1; + } + + /// + /// Sets current rectangle to the union of the current rectangle and the specified point. + /// + public void Union(XPoint point) + { + Union(new XRect(point, point)); + } + + /// + /// Returns the intersection of a rectangle and a point. + /// + public static XRect Union(XRect rect, XPoint point) + { + rect.Union(new XRect(point, point)); + return rect; + } + + /// + /// Moves a rectangle by the specified amount. + /// + public void Offset(XVector offsetVector) + { + if (IsEmpty) + throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); + _x += offsetVector.X; + _y += offsetVector.Y; + } + + /// + /// Moves a rectangle by the specified amount. + /// + public void Offset(double offsetX, double offsetY) + { + if (IsEmpty) + throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); + _x += offsetX; + _y += offsetY; + } + + /// + /// Returns a rectangle that is offset from the specified rectangle by using the specified vector. + /// + public static XRect Offset(XRect rect, XVector offsetVector) + { + rect.Offset(offsetVector.X, offsetVector.Y); + return rect; + } + + /// + /// Returns a rectangle that is offset from the specified rectangle by using specified horizontal and vertical amounts. + /// + public static XRect Offset(XRect rect, double offsetX, double offsetY) + { + rect.Offset(offsetX, offsetY); + return rect; + } + + /// + /// Translates the rectangle by adding the specified point. + /// + //[Obsolete("Use Offset.")] + public static XRect operator +(XRect rect, XPoint point) + { + return new XRect(rect._x + point.X, rect.Y + point.Y, rect._width, rect._height); + } + + /// + /// Translates the rectangle by subtracting the specified point. + /// + //[Obsolete("Use Offset.")] + public static XRect operator -(XRect rect, XPoint point) + { + return new XRect(rect._x - point.X, rect.Y - point.Y, rect._width, rect._height); + } + + /// + /// Expands the rectangle by using the specified Size, in all directions. + /// + public void Inflate(XSize size) + { + Inflate(size.Width, size.Height); + } + + /// + /// Expands or shrinks the rectangle by using the specified width and height amounts, in all directions. + /// + public void Inflate(double width, double height) + { + if (IsEmpty) + throw new InvalidOperationException("CannotCallMethod"); //SR.Get(SRID.Rect_CannotCallMethod, new object[0])); + _x -= width; + _y -= height; + _width += width; + _width += width; + _height += height; + _height += height; + if (_width < 0 || _height < 0) + this = s_empty; + } + + /// + /// Returns the rectangle that results from expanding the specified rectangle by the specified Size, in all directions. + /// + public static XRect Inflate(XRect rect, XSize size) + { + rect.Inflate(size.Width, size.Height); + return rect; + } + + /// + /// Creates a rectangle that results from expanding or shrinking the specified rectangle by the specified width and height amounts, in all directions. + /// + public static XRect Inflate(XRect rect, double width, double height) + { + rect.Inflate(width, height); + return rect; + } + + /// + /// Returns the rectangle that results from applying the specified matrix to the specified rectangle. + /// + public static XRect Transform(XRect rect, XMatrix matrix) + { + XMatrix.MatrixHelper.TransformRect(ref rect, ref matrix); + return rect; + } + + /// + /// Transforms the rectangle by applying the specified matrix. + /// + public void Transform(XMatrix matrix) + { + XMatrix.MatrixHelper.TransformRect(ref this, ref matrix); + } + + /// + /// Multiplies the size of the current rectangle by the specified x and y values. + /// + public void Scale(double scaleX, double scaleY) + { + if (!IsEmpty) + { + _x *= scaleX; + _y *= scaleY; + _width *= scaleX; + _height *= scaleY; + if (scaleX < 0) + { + _x += _width; + _width *= -1.0; + } + if (scaleY < 0) + { + _y += _height; + _height *= -1.0; + } + } + } + +#if CORE // Internal version in CORE build. +#if UseGdiObjects + /// + /// Converts this instance to a System.Drawing.RectangleF. + /// + internal RectangleF ToRectangleF() + { + return new RectangleF((float)_x, (float)_y, (float)_width, (float)_height); + } +#endif +#endif + +#if GDI + /// + /// Converts this instance to a System.Drawing.RectangleF. + /// + public RectangleF ToRectangleF() + { + return new RectangleF((float)_x, (float)_y, (float)_width, (float)_height); + } +#endif + +#if GDI + /// + /// Performs an implicit conversion from a System.Drawing.Rectangle to an XRect. + /// + public static implicit operator XRect(Rectangle rect) + { + return new XRect(rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Performs an implicit conversion from a System.Drawing.RectangleF to an XRect. + /// + public static implicit operator XRect(RectangleF rect) + { + return new XRect(rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Performs an implicit conversion from System.Windows.Rect to XRect. + /// + public static implicit operator XRect(SysRect rect) + { + return new XRect(rect.X, rect.Y, rect.Width, rect.Height); + } +#endif + + bool ContainsInternal(double x, double y) + { + return x >= _x && x - _width <= _x && y >= _y && y - _height <= _y; + } + + static XRect CreateEmptyRect() + { + XRect rect = new XRect(); + rect._x = double.PositiveInfinity; + rect._y = double.PositiveInfinity; + rect._width = double.NegativeInfinity; + rect._height = double.NegativeInfinity; + return rect; + } + + static XRect() + { + s_empty = CreateEmptyRect(); + } + + static readonly XRect s_empty; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return String.Format(CultureInfo.InvariantCulture, + "rect=({0:" + format + "}, {1:" + format + "}, {2:" + format + "}, {3:" + format + "})", + _x, _y, _width, _height); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XSize.cs b/src/PDFsharp/src/PdfSharp/Drawing/XSize.cs new file mode 100644 index 00000000..452026c4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XSize.cs @@ -0,0 +1,376 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +using SysPoint = System.Windows.Point; +using SysSize = System.Windows.Size; +#endif +#if NETFX_CORE +using Windows.UI.Xaml.Media; +using SysPoint = Windows.Foundation.Point; +using SysSize = Windows.Foundation.Size; +#endif +#if !EDF_CORE +using PdfSharp.Internal; +#else +using PdfSharp.Internal; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents a pair of floating-point numbers, typically the width and height of a + /// graphical object. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + [Serializable, StructLayout(LayoutKind.Sequential)] //, ValueSerializer(typeof(SizeValueSerializer)), TypeConverter(typeof(SizeConverter))] + public struct XSize : IFormattable + { + /// + /// Initializes a new instance of the XPoint class with the specified values. + /// + public XSize(double width, double height) + { + if (width < 0 || height < 0) + throw new ArgumentException("WidthAndHeightCannotBeNegative"); //SR.Get(SRID.Size_WidthAndHeightCannotBeNegative, new object[0])); + + _width = width; + _height = height; + } + + /// + /// Determines whether two size objects are equal. + /// + public static bool operator ==(XSize size1, XSize size2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return size1.Width == size2.Width && size1.Height == size2.Height; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Determines whether two size objects are not equal. + /// + public static bool operator !=(XSize size1, XSize size2) + { + return !(size1 == size2); + } + + /// + /// Indicates whether this two instance are equal. + /// + public static bool Equals(XSize size1, XSize size2) + { + if (size1.IsEmpty) + return size2.IsEmpty; + return size1.Width.Equals(size2.Width) && size1.Height.Equals(size2.Height); + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + public override bool Equals(object o) + { + if (!(o is XSize)) + return false; + return Equals(this, (XSize)o); + } + + /// + /// Indicates whether this instance and a specified size are equal. + /// + public bool Equals(XSize value) + { + return Equals(this, value); + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + if (IsEmpty) + return 0; + return Width.GetHashCode() ^ Height.GetHashCode(); + } + + /// + /// Parses the size from a string. + /// + public static XSize Parse(string source) + { + XSize empty; + CultureInfo cultureInfo = CultureInfo.InvariantCulture; + TokenizerHelper helper = new TokenizerHelper(source, cultureInfo); + string str = helper.NextTokenRequired(); + if (str == "Empty") + empty = Empty; + else + empty = new XSize(Convert.ToDouble(str, cultureInfo), Convert.ToDouble(helper.NextTokenRequired(), cultureInfo)); + helper.LastTokenRequired(); + return empty; + } + +#if GDI + /// + /// Converts this XSize to a PointF. + /// + public PointF ToPointF() + { + return new PointF((float)_width, (float)_height); + } +#endif + + /// + /// Converts this XSize to an XPoint. + /// + public XPoint ToXPoint() + { + return new XPoint(_width, _height); + } + + /// + /// Converts this XSize to an XVector. + /// + public XVector ToXVector() + { + return new XVector(_width, _height); + } + +#if GDI + /// + /// Converts this XSize to a SizeF. + /// + public SizeF ToSizeF() + { + return new SizeF((float)_width, (float)_height); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Converts this XSize to a System.Windows.Size. + /// + public SysSize ToSize() + { + return new SysSize(_width, _height); + } +#endif + +#if GDI + /// + /// Creates an XSize from a System.Drawing.Size. + /// + public static XSize FromSize(System.Drawing.Size size) + { + return new XSize(size.Width, size.Height); + } + + /// + /// Implicit conversion from XSize to System.Drawing.Size. The conversion must be implicit because the + /// WinForms designer uses it. + /// + public static implicit operator XSize(System.Drawing.Size size) + { + return new XSize(size.Width, size.Height); + } +#endif + +#if WPF || NETFX_CORE + /// + /// Creates an XSize from a System.Drawing.Size. + /// + public static XSize FromSize(SysSize size) + { + return new XSize(size.Width, size.Height); + } +#endif + +#if GDI + /// + /// Creates an XSize from a System.Drawing.Size. + /// + public static XSize FromSizeF(SizeF size) + { + return new XSize(size.Width, size.Height); + } +#endif + + /// + /// Converts this XSize to a human readable string. + /// + public override string ToString() + { + return ConvertToString(null, null); + } + + /// + /// Converts this XSize to a human readable string. + /// + public string ToString(IFormatProvider provider) + { + return ConvertToString(null, provider); + } + + /// + /// Converts this XSize to a human readable string. + /// + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + if (IsEmpty) + return "Empty"; + + char numericListSeparator = TokenizerHelper.GetNumericListSeparator(provider); + provider = provider ?? CultureInfo.InvariantCulture; + // ReSharper disable FormatStringProblem + return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}", new object[] { numericListSeparator, _width, _height }); + // ReSharper restore FormatStringProblem + } + + /// + /// Returns an empty size, i.e. a size with a width or height less than 0. + /// + public static XSize Empty + { + get { return s_empty; } + } + static readonly XSize s_empty; + + /// + /// Gets a value indicating whether this instance is empty. + /// + public bool IsEmpty + { + get { return _width < 0; } + } + + /// + /// Gets or sets the width. + /// + public double Width + { + get { return _width; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptySize"); //SR.Get(SRID.Size_CannotModifyEmptySize, new object[0])); + if (value < 0) + throw new ArgumentException("WidthCannotBeNegative"); //SR.Get(SRID.Size_WidthCannotBeNegative, new object[0])); + _width = value; + } + } + double _width; + + /// + /// Gets or sets the height. + /// + public double Height + { + get { return _height; } + set + { + if (IsEmpty) + throw new InvalidOperationException("CannotModifyEmptySize"); // SR.Get(SRID.Size_CannotModifyEmptySize, new object[0])); + if (value < 0) + throw new ArgumentException("HeightCannotBeNegative"); //SR.Get(SRID.Size_HeightCannotBeNegative, new object[0])); + _height = value; + } + } + double _height; + + /// + /// Performs an explicit conversion from XSize to XVector. + /// + public static explicit operator XVector(XSize size) + { + return new XVector(size._width, size._height); + } + + /// + /// Performs an explicit conversion from XSize to XPoint. + /// + public static explicit operator XPoint(XSize size) + { + return new XPoint(size._width, size._height); + } + +#if WPF || NETFX_CORE + /// + /// Performs an explicit conversion from Size to XSize. + /// + public static explicit operator XSize(SysSize size) + { + return new XSize(size.Width, size.Height); + } +#endif + + private static XSize CreateEmptySize() + { + XSize size = new XSize(); + size._width = double.NegativeInfinity; + size._height = double.NegativeInfinity; + return size; + } + + static XSize() + { + s_empty = CreateEmptySize(); + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return String.Format(CultureInfo.InvariantCulture, + "size=({2}{0:" + format + "}, {1:" + format + "})", + _width, _height, IsEmpty ? "Empty " : ""); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XSolidBrush.cs b/src/PDFsharp/src/PdfSharp/Drawing/XSolidBrush.cs new file mode 100644 index 00000000..276a4f75 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XSolidBrush.cs @@ -0,0 +1,178 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif +#if UWP +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using UwpBrush = Windows.UI.Xaml.Media.Brush; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Defines a single color object used to fill shapes and draw text. + /// + public sealed class XSolidBrush : XBrush + { + /// + /// Initializes a new instance of the class. + /// + public XSolidBrush() + { } + + /// + /// Initializes a new instance of the class. + /// + public XSolidBrush(XColor color) + : this(color, false) + { } + + internal XSolidBrush(XColor color, bool immutable) + { + _color = color; + _immutable = immutable; + } + + /// + /// Initializes a new instance of the class. + /// + public XSolidBrush(XSolidBrush brush) + { + _color = brush.Color; + } + + /// + /// Gets or sets the color of this brush. + /// + public XColor Color + { + get { return _color; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XSolidBrush")); +#if GDI + _gdiDirty = _gdiDirty || _color != value; +#endif +#if WPF + _wpfDirty = _wpfDirty || _color != value; +#endif +#if GDI && WPF + _gdiDirty = _wpfDirty = true; +#endif + _color = value; + } + } + internal XColor _color; + + /// + /// Gets or sets a value indicating whether the brush enables overprint when used in a PDF document. + /// Experimental, takes effect only on CMYK color mode. + /// + public bool Overprint + { + get { return _overprint; } + set + { + if (_immutable) + throw new ArgumentException(PSSR.CannotChangeImmutableObject("XSolidBrush")); + _overprint = value; + } + } + internal bool _overprint; + +#if GDI + internal override System.Drawing.Brush RealizeGdiBrush() + { + if (_gdiDirty) + { + if (_gdiBrush == null) + _gdiBrush = new SolidBrush(_color.ToGdiColor()); + else + _gdiBrush.Color = _color.ToGdiColor(); + _gdiDirty = false; + } + +#if DEBUG + System.Drawing.Color clr = _color.ToGdiColor(); + SolidBrush brush1 = new SolidBrush(clr); + Debug.Assert(_gdiBrush.Color == brush1.Color); +#endif + return _gdiBrush; + } +#endif + +#if WPF + internal override System.Windows.Media.Brush RealizeWpfBrush() + { + if (_wpfDirty) + { + if (_wpfBrush == null) + _wpfBrush = new SolidColorBrush(_color.ToWpfColor()); + else + _wpfBrush.Color = _color.ToWpfColor(); + _wpfDirty = false; + } + +#if DEBUG_ + System.Windows.Media.Color clr = _color.ToWpfColor(); + System.Windows.Media.SolidColorBrush brush1 = new System.Windows.Media.SolidColorBrush(clr); //System.Drawing.Color.FromArgb(128, 128, 0, 0)); + Debug.Assert(_wpfBrush.Color == brush1.Color); // Crashes during unit testing +#endif + return _wpfBrush; + } +#endif + +#if UWP + internal override ICanvasBrush RealizeCanvasBrush() + { + return new CanvasSolidColorBrush(CanvasDevice.GetSharedDevice(), _color.ToUwpColor()); + } +#endif + +#if GDI + bool _gdiDirty = true; + SolidBrush _gdiBrush; +#endif +#if WPF + bool _wpfDirty = true; + SolidColorBrush _wpfBrush; +#endif + readonly bool _immutable; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XStringFormat.cs b/src/PDFsharp/src/PdfSharp/Drawing/XStringFormat.cs new file mode 100644 index 00000000..17afaa77 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XStringFormat.cs @@ -0,0 +1,223 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if CORE +#endif +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ +#if true_ + /// + /// Not used in this implementation. + /// + [Flags] + public enum XStringFormatFlags + { + //DirectionRightToLeft = 0x0001, + //DirectionVertical = 0x0002, + //FitBlackBox = 0x0004, + //DisplayFormatControl = 0x0020, + //NoFontFallback = 0x0400, + /// + /// The default value. + /// + MeasureTrailingSpaces = 0x0800, + //NoWrap = 0x1000, + //LineLimit = 0x2000, + //NoClip = 0x4000, + } +#endif + + /// + /// Represents the text layout information. + /// + public class XStringFormat + { + /// + /// Initializes a new instance of the class. + /// + public XStringFormat() + { +#if WPF + GetType(); // Make ReSharper happy. +#endif + } + + //TODO public StringFormat(StringFormat format); + //public StringFormat(StringFormatFlags options); + //public StringFormat(StringFormatFlags options, int language); + //public object Clone(); + //public void Dispose(); + //private void Dispose(bool disposing); + //protected override void Finalize(); + //public float[] GetTabStops(out float firstTabOffset); + //public void SetDigitSubstitution(int language, StringDigitSubstitute substitute); + //public void SetMeasurableCharacterRanges(CharacterRange[] ranges); + //public void SetTabStops(float firstTabOffset, float[] tabStops); + //public override string ToString(); + + /// + /// Gets or sets horizontal text alignment information. + /// + public XStringAlignment Alignment + { + get { return _alignment; } + set + { + _alignment = value; +#if CORE || GDI +#if UseGdiObjects + // Update StringFormat only if it exists. + if (_stringFormat != null) + { + _stringFormat.Alignment = (StringAlignment)value; + } +#endif +#endif + } + } + XStringAlignment _alignment; + + //public int DigitSubstitutionLanguage { get; } + //public StringDigitSubstitute DigitSubstitutionMethod { get; } + //public StringFormatFlags FormatFlags { get; set; } + //public static StringFormat GenericDefault { get; } + //public static StringFormat GenericTypographic { get; } + //public HotkeyPrefix HotkeyPrefix { get; set; } + + /// + /// Gets or sets the line alignment. + /// + public XLineAlignment LineAlignment + { + get { return _lineAlignment; } + set + { + _lineAlignment = value; +#if CORE || GDI +#if UseGdiObjects + // Update StringFormat only if it exists. + if (_stringFormat != null) + { + // BaseLine is specific to PDFsharp. + if (value == XLineAlignment.BaseLine) + _stringFormat.LineAlignment = StringAlignment.Near; + else + _stringFormat.LineAlignment = (StringAlignment)value; + } +#endif +#endif + } + } + XLineAlignment _lineAlignment; + + //public StringTrimming Trimming { get; set; } + + /// + /// Gets a new XStringFormat object that aligns the text left on the base line. + /// + [Obsolete("Use XStringFormats.Default. (Note plural in class name.)")] + public static XStringFormat Default + { + get { return XStringFormats.Default; } + } + + /// + /// Gets a new XStringFormat object that aligns the text top left of the layout rectangle. + /// + [Obsolete("Use XStringFormats.Default. (Note plural in class name.)")] + public static XStringFormat TopLeft + { + get { return XStringFormats.TopLeft; } + } + + /// + /// Gets a new XStringFormat object that centers the text in the middle of the layout rectangle. + /// + [Obsolete("Use XStringFormats.Center. (Note plural in class name.)")] + public static XStringFormat Center + { + get { return XStringFormats.Center; } + } + + /// + /// Gets a new XStringFormat object that centers the text at the top of the layout rectangle. + /// + [Obsolete("Use XStringFormats.TopCenter. (Note plural in class name.)")] + public static XStringFormat TopCenter + { + get { return XStringFormats.TopCenter; } + } + + /// + /// Gets a new XStringFormat object that centers the text at the bottom of the layout rectangle. + /// + [Obsolete("Use XStringFormats.BottomCenter. (Note plural in class name.)")] + public static XStringFormat BottomCenter + { + get { return XStringFormats.BottomCenter; } + } + +#if GDI + //#if UseGdiObjects + internal StringFormat RealizeGdiStringFormat() + { + if (_stringFormat == null) + { + // It seems that StringFormat.GenericTypographic is a global object and we need "Clone()" to avoid side effects. + _stringFormat = (StringFormat)StringFormat.GenericTypographic.Clone(); + _stringFormat.Alignment = (StringAlignment)_alignment; + + // BaseLine is specific to PDFsharp. + if (_lineAlignment == XLineAlignment.BaseLine) + _stringFormat.LineAlignment = StringAlignment.Near; + else + _stringFormat.LineAlignment = (StringAlignment)_lineAlignment; + + //_stringFormat.FormatFlags = (StringFormatFlags)_formatFlags; + + // Bugfix: Set MeasureTrailingSpaces to get the correct width with Graphics.MeasureString(). + // Before, MeasureString() didn't include blanks in width calculation, which could result in text overflowing table or page border before wrapping. $MaOs + _stringFormat.FormatFlags = _stringFormat.FormatFlags | StringFormatFlags.MeasureTrailingSpaces; + } + return _stringFormat; + } + StringFormat _stringFormat; + //#endif +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XStringFormats.cs b/src/PDFsharp/src/PdfSharp/Drawing/XStringFormats.cs new file mode 100644 index 00000000..58d80d37 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XStringFormats.cs @@ -0,0 +1,235 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ + /// + /// Represents predefined text layouts. + /// + public static class XStringFormats + { + /// + /// Gets a new XStringFormat object that aligns the text left on the base line. + /// This is the same as BaseLineLeft. + /// + public static XStringFormat Default + { + get { return BaseLineLeft; } + } + + /// + /// Gets a new XStringFormat object that aligns the text left on the base line. + /// This is the same as Default. + /// + public static XStringFormat BaseLineLeft + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Near; + format.LineAlignment = XLineAlignment.BaseLine; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text top left of the layout rectangle. + /// + public static XStringFormat TopLeft + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Near; + format.LineAlignment = XLineAlignment.Near; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text center left of the layout rectangle. + /// + public static XStringFormat CenterLeft + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Near; + format.LineAlignment = XLineAlignment.Center; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text bottom left of the layout rectangle. + /// + public static XStringFormat BottomLeft + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Near; + format.LineAlignment = XLineAlignment.Far; + return format; + } + } + + /// + /// Gets a new XStringFormat object that centers the text in the middle of the base line. + /// + public static XStringFormat BaseLineCenter + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Center; + format.LineAlignment = XLineAlignment.BaseLine; + return format; + } + } + + /// + /// Gets a new XStringFormat object that centers the text at the top of the layout rectangle. + /// + public static XStringFormat TopCenter + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Center; + format.LineAlignment = XLineAlignment.Near; + return format; + } + } + + /// + /// Gets a new XStringFormat object that centers the text in the middle of the layout rectangle. + /// + public static XStringFormat Center + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Center; + format.LineAlignment = XLineAlignment.Center; + return format; + } + } + + /// + /// Gets a new XStringFormat object that centers the text at the bottom of the layout rectangle. + /// + public static XStringFormat BottomCenter + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Center; + format.LineAlignment = XLineAlignment.Far; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text in right on the base line. + /// + public static XStringFormat BaseLineRight + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Far; + format.LineAlignment = XLineAlignment.BaseLine; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text top right of the layout rectangle. + /// + public static XStringFormat TopRight + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Far; + format.LineAlignment = XLineAlignment.Near; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text center right of the layout rectangle. + /// + public static XStringFormat CenterRight + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Far; + format.LineAlignment = XLineAlignment.Center; + return format; + } + } + + /// + /// Gets a new XStringFormat object that aligns the text at the bottom right of the layout rectangle. + /// + public static XStringFormat BottomRight + { + get + { + // Create new format to allow changes. + XStringFormat format = new XStringFormat(); + format.Alignment = XStringAlignment.Far; + format.LineAlignment = XLineAlignment.Far; + return format; + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XTypeface.cs b/src/PDFsharp/src/PdfSharp/Drawing/XTypeface.cs new file mode 100644 index 00000000..186eb9cd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XTypeface.cs @@ -0,0 +1,84 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +#endif + +namespace PdfSharp.Drawing +{ +#if true_ // Not yet used + /// + /// no: Specifies a physical font face that corresponds to a font file on the disk or in memory. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal class XTypeface_not_yet_used + { + public XTypeface_not_yet_used(XFontFamily family, XFontStyle style) + { + _family = family; + _style = style; + } + + public XFontFamily Family + { + get { return _family; } + } + XFontFamily _family; + + public XFontStyle Style + { + get { return _style; } + } + XFontStyle _style; + + public bool TryGetGlyphTypeface(out XGlyphTypeface glyphTypeface) + { + glyphTypeface = null; + return false; + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get { return string.Format(CultureInfo.InvariantCulture, "XTypeface"); } + } + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XUnit.cs b/src/PDFsharp/src/PdfSharp/Drawing/XUnit.cs new file mode 100644 index 00000000..59ce5b5b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XUnit.cs @@ -0,0 +1,597 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; + +namespace PdfSharp.Drawing +{ + /// + /// Represents a value and its unit of measure. The structure converts implicitly from and to + /// double with a value measured in point. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public struct XUnit : IFormattable + { + internal const double PointFactor = 1; + internal const double InchFactor = 72; + internal const double MillimeterFactor = 72 / 25.4; + internal const double CentimeterFactor = 72 / 2.54; + internal const double PresentationFactor = 72 / 96.0; + + internal const double PointFactorWpf = 96 / 72.0; + internal const double InchFactorWpf = 96; + internal const double MillimeterFactorWpf = 96 / 25.4; + internal const double CentimeterFactorWpf = 96 / 2.54; + internal const double PresentationFactorWpf = 1; + + /// + /// Initializes a new instance of the XUnit class with type set to point. + /// + public XUnit(double point) + { + _value = point; + _type = XGraphicsUnit.Point; + } + + /// + /// Initializes a new instance of the XUnit class. + /// + public XUnit(double value, XGraphicsUnit type) + { + if (!Enum.IsDefined(typeof(XGraphicsUnit), type)) +#if !SILVERLIGHT && !NETFX_CORE && !UWP + throw new System.ComponentModel.InvalidEnumArgumentException(nameof(type), (int)type, typeof(XGraphicsUnit)); +#else + throw new ArgumentException("type"); +#endif + _value = value; + _type = type; + } + + /// + /// Gets the raw value of the object without any conversion. + /// To determine the XGraphicsUnit use property Type. + /// To get the value in point use the implicit conversion to double. + /// + public double Value + { + get { return _value; } + } + + /// + /// Gets the unit of measure. + /// + public XGraphicsUnit Type + { + get { return _type; } + } + + /// + /// Gets or sets the value in point. + /// + public double Point + { + get + { + switch (_type) + { + case XGraphicsUnit.Point: + return _value; + + case XGraphicsUnit.Inch: + return _value * 72; + + case XGraphicsUnit.Millimeter: + return _value * 72 / 25.4; + + case XGraphicsUnit.Centimeter: + return _value * 72 / 2.54; + + case XGraphicsUnit.Presentation: + return _value * 72 / 96; + + default: + throw new InvalidCastException(); + } + } + set + { + _value = value; + _type = XGraphicsUnit.Point; + } + } + + /// + /// Gets or sets the value in inch. + /// + public double Inch + { + get + { + switch (_type) + { + case XGraphicsUnit.Point: + return _value / 72; + + case XGraphicsUnit.Inch: + return _value; + + case XGraphicsUnit.Millimeter: + return _value / 25.4; + + case XGraphicsUnit.Centimeter: + return _value / 2.54; + + case XGraphicsUnit.Presentation: + return _value / 96; + + default: + throw new InvalidCastException(); + } + } + set + { + _value = value; + _type = XGraphicsUnit.Inch; + } + } + + /// + /// Gets or sets the value in millimeter. + /// + public double Millimeter + { + get + { + switch (_type) + { + case XGraphicsUnit.Point: + return _value * 25.4 / 72; + + case XGraphicsUnit.Inch: + return _value * 25.4; + + case XGraphicsUnit.Millimeter: + return _value; + + case XGraphicsUnit.Centimeter: + return _value * 10; + + case XGraphicsUnit.Presentation: + return _value * 25.4 / 96; + + default: + throw new InvalidCastException(); + } + } + set + { + _value = value; + _type = XGraphicsUnit.Millimeter; + } + } + + /// + /// Gets or sets the value in centimeter. + /// + public double Centimeter + { + get + { + switch (_type) + { + case XGraphicsUnit.Point: + return _value * 2.54 / 72; + + case XGraphicsUnit.Inch: + return _value * 2.54; + + case XGraphicsUnit.Millimeter: + return _value / 10; + + case XGraphicsUnit.Centimeter: + return _value; + + case XGraphicsUnit.Presentation: + return _value * 2.54 / 96; + + default: + throw new InvalidCastException(); + } + } + set + { + _value = value; + _type = XGraphicsUnit.Centimeter; + } + } + + /// + /// Gets or sets the value in presentation units (1/96 inch). + /// + public double Presentation + { + get + { + switch (_type) + { + case XGraphicsUnit.Point: + return _value * 96 / 72; + + case XGraphicsUnit.Inch: + return _value * 96; + + case XGraphicsUnit.Millimeter: + return _value * 96 / 25.4; + + case XGraphicsUnit.Centimeter: + return _value * 96 / 2.54; + + case XGraphicsUnit.Presentation: + return _value; + + default: + throw new InvalidCastException(); + } + } + set + { + _value = value; + _type = XGraphicsUnit.Point; + } + } + + /// + /// Returns the object as string using the format information. + /// The unit of measure is appended to the end of the string. + /// + public string ToString(IFormatProvider formatProvider) + { + string valuestring = _value.ToString(formatProvider) + GetSuffix(); + return valuestring; + } + + /// + /// Returns the object as string using the specified format and format information. + /// The unit of measure is appended to the end of the string. + /// + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + string valuestring = _value.ToString(format, formatProvider) + GetSuffix(); + return valuestring; + } + + /// + /// Returns the object as string. The unit of measure is appended to the end of the string. + /// + public override string ToString() + { + string valuestring = _value.ToString(CultureInfo.InvariantCulture) + GetSuffix(); + return valuestring; + } + + /// + /// Returns the unit of measure of the object as a string like 'pt', 'cm', or 'in'. + /// + string GetSuffix() + { + switch (_type) + { + case XGraphicsUnit.Point: + return "pt"; + + case XGraphicsUnit.Inch: + return "in"; + + case XGraphicsUnit.Millimeter: + return "mm"; + + case XGraphicsUnit.Centimeter: + return "cm"; + + case XGraphicsUnit.Presentation: + return "pu"; + + //case XGraphicsUnit.Pica: + // return "pc"; + + //case XGraphicsUnit.Line: + // return "li"; + + default: + throw new InvalidCastException(); + } + } + + /// + /// Returns an XUnit object. Sets type to point. + /// + public static XUnit FromPoint(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Point; + return unit; + } + + /// + /// Returns an XUnit object. Sets type to inch. + /// + public static XUnit FromInch(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Inch; + return unit; + } + + /// + /// Returns an XUnit object. Sets type to millimeters. + /// + public static XUnit FromMillimeter(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Millimeter; + return unit; + } + + /// + /// Returns an XUnit object. Sets type to centimeters. + /// + public static XUnit FromCentimeter(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Centimeter; + return unit; + } + + /// + /// Returns an XUnit object. Sets type to Presentation. + /// + public static XUnit FromPresentation(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Presentation; + return unit; + } + + /// + /// Converts a string to an XUnit object. + /// If the string contains a suffix like 'cm' or 'in' the object will be converted + /// to the appropriate type, otherwise point is assumed. + /// + public static implicit operator XUnit(string value) + { + XUnit unit; + value = value.Trim(); + + // HACK for Germans... + value = value.Replace(',', '.'); + + int count = value.Length; + int valLen = 0; + for (; valLen < count;) + { + char ch = value[valLen]; + if (ch == '.' || ch == '-' || ch == '+' || char.IsNumber(ch)) + valLen++; + else + break; + } + + try + { + unit._value = Double.Parse(value.Substring(0, valLen).Trim(), CultureInfo.InvariantCulture); + } + catch (Exception ex) + { + unit._value = 1; + string message = String.Format("String '{0}' is not a valid value for structure 'XUnit'.", value); + throw new ArgumentException(message, ex); + } + + string typeStr = value.Substring(valLen).Trim().ToLower(); + unit._type = XGraphicsUnit.Point; + switch (typeStr) + { + case "cm": + unit._type = XGraphicsUnit.Centimeter; + break; + + case "in": + unit._type = XGraphicsUnit.Inch; + break; + + case "mm": + unit._type = XGraphicsUnit.Millimeter; + break; + + case "": + case "pt": + unit._type = XGraphicsUnit.Point; + break; + + case "pu": // presentation units + unit._type = XGraphicsUnit.Presentation; + break; + + default: + throw new ArgumentException("Unknown unit type: '" + typeStr + "'"); + } + return unit; + } + + /// + /// Converts an int to an XUnit object with type set to point. + /// + public static implicit operator XUnit(int value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Point; + return unit; + } + + /// + /// Converts a double to an XUnit object with type set to point. + /// + public static implicit operator XUnit(double value) + { + XUnit unit; + unit._value = value; + unit._type = XGraphicsUnit.Point; + return unit; + } + + /// + /// Returns a double value as point. + /// + public static implicit operator double(XUnit value) + { + return value.Point; + } + + /// + /// Memberwise comparison. To compare by value, + /// use code like Math.Abs(a.Pt - b.Pt) < 1e-5. + /// + public static bool operator ==(XUnit value1, XUnit value2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return value1._type == value2._type && value1._value == value2._value; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Memberwise comparison. To compare by value, + /// use code like Math.Abs(a.Pt - b.Pt) < 1e-5. + /// + public static bool operator !=(XUnit value1, XUnit value2) + { + return !(value1 == value2); + } + + /// + /// Calls base class Equals. + /// + public override bool Equals(Object obj) + { + if (obj is XUnit) + return this == (XUnit)obj; + return false; + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + // ReSharper disable NonReadonlyFieldInGetHashCode + return _value.GetHashCode() ^ _type.GetHashCode(); + // ReSharper restore NonReadonlyFieldInGetHashCode + } + + /// + /// This member is intended to be used by XmlDomainObjectReader only. + /// + public static XUnit Parse(string value) + { + XUnit unit = value; + return unit; + } + + /// + /// Converts an existing object from one unit into another unit type. + /// + public void ConvertType(XGraphicsUnit type) + { + if (_type == type) + return; + + switch (type) + { + case XGraphicsUnit.Point: + _value = Point; + _type = XGraphicsUnit.Point; + break; + + case XGraphicsUnit.Inch: + _value = Inch; + _type = XGraphicsUnit.Inch; + break; + + case XGraphicsUnit.Centimeter: + _value = Centimeter; + _type = XGraphicsUnit.Centimeter; + break; + + case XGraphicsUnit.Millimeter: + _value = Millimeter; + _type = XGraphicsUnit.Millimeter; + break; + + case XGraphicsUnit.Presentation: + _value = Presentation; + _type = XGraphicsUnit.Presentation; + break; + + default: + throw new ArgumentException("Unknown unit type: '" + type + "'"); + } + } + + /// + /// Represents a unit with all values zero. + /// + public static readonly XUnit Zero = new XUnit(); + + double _value; + XGraphicsUnit _type; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return String.Format(CultureInfo.InvariantCulture, "unit=({0:" + format + "} {1})", _value, GetSuffix()); + } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Drawing/XVector.cs b/src/PDFsharp/src/PdfSharp/Drawing/XVector.cs new file mode 100644 index 00000000..ced48a4f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/XVector.cs @@ -0,0 +1,299 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using PdfSharp.Internal; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif + +#pragma warning disable 1591 + +#if !EDF_CORE +namespace PdfSharp.Drawing +#else +namespace Edf.Drawing +#endif +{ + /// + /// Represents a two-dimensional vector specified by x- and y-coordinates. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct XVector : IFormattable + { + public XVector(double x, double y) + { + _x = x; + _y = y; + } + + public static bool operator ==(XVector vector1, XVector vector2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return vector1._x == vector2._x && vector1._y == vector2._y; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + public static bool operator !=(XVector vector1, XVector vector2) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return vector1._x != vector2._x || vector1._y != vector2._y; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + public static bool Equals(XVector vector1, XVector vector2) + { + if (vector1.X.Equals(vector2.X)) + return vector1.Y.Equals(vector2.Y); + return false; + } + + public override bool Equals(object o) + { + if (!(o is XVector)) + return false; + return Equals(this, (XVector)o); + } + + public bool Equals(XVector value) + { + return Equals(this, value); + } + + public override int GetHashCode() + { + // ReSharper disable NonReadonlyFieldInGetHashCode + return _x.GetHashCode() ^ _y.GetHashCode(); + // ReSharper restore NonReadonlyFieldInGetHashCode + } + + public static XVector Parse(string source) + { + TokenizerHelper helper = new TokenizerHelper(source, CultureInfo.InvariantCulture); + string str = helper.NextTokenRequired(); + XVector vector = new XVector(Convert.ToDouble(str, CultureInfo.InvariantCulture), Convert.ToDouble(helper.NextTokenRequired(), CultureInfo.InvariantCulture)); + helper.LastTokenRequired(); + return vector; + } + + public double X + { + get { return _x; } + set { _x = value; } + } + double _x; + + public double Y + { + get { return _y; } + set { _y = value; } + } + double _y; + + public override string ToString() + { + return ConvertToString(null, null); + } + + public string ToString(IFormatProvider provider) + { + return ConvertToString(null, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + return ConvertToString(format, provider); + } + + internal string ConvertToString(string format, IFormatProvider provider) + { + const char numericListSeparator = ','; + provider = provider ?? CultureInfo.InvariantCulture; + // ReSharper disable once FormatStringProblem + return string.Format(provider, "{1:" + format + "}{0}{2:" + format + "}", numericListSeparator, _x, _y); + } + + public double Length + { + get { return Math.Sqrt(_x * _x + _y * _y); } + } + + public double LengthSquared + { + get { return _x * _x + _y * _y; } + } + + public void Normalize() + { + this = this / Math.Max(Math.Abs(_x), Math.Abs(_y)); + this = this / Length; + } + + public static double CrossProduct(XVector vector1, XVector vector2) + { + return vector1._x * vector2._y - vector1._y * vector2._x; + } + + public static double AngleBetween(XVector vector1, XVector vector2) + { + double y = vector1._x * vector2._y - vector2._x * vector1._y; + double x = vector1._x * vector2._x + vector1._y * vector2._y; + return (Math.Atan2(y, x) * 57.295779513082323); + } + + public static XVector operator -(XVector vector) + { + return new XVector(-vector._x, -vector._y); + } + + public void Negate() + { + _x = -_x; + _y = -_y; + } + + public static XVector operator +(XVector vector1, XVector vector2) + { + return new XVector(vector1._x + vector2._x, vector1._y + vector2._y); + } + + public static XVector Add(XVector vector1, XVector vector2) + { + return new XVector(vector1._x + vector2._x, vector1._y + vector2._y); + } + + public static XVector operator -(XVector vector1, XVector vector2) + { + return new XVector(vector1._x - vector2._x, vector1._y - vector2._y); + } + + public static XVector Subtract(XVector vector1, XVector vector2) + { + return new XVector(vector1._x - vector2._x, vector1._y - vector2._y); + } + + public static XPoint operator +(XVector vector, XPoint point) + { + return new XPoint(point.X + vector._x, point.Y + vector._y); + } + + public static XPoint Add(XVector vector, XPoint point) + { + return new XPoint(point.X + vector._x, point.Y + vector._y); + } + + public static XVector operator *(XVector vector, double scalar) + { + return new XVector(vector._x * scalar, vector._y * scalar); + } + + public static XVector Multiply(XVector vector, double scalar) + { + return new XVector(vector._x * scalar, vector._y * scalar); + } + + public static XVector operator *(double scalar, XVector vector) + { + return new XVector(vector._x * scalar, vector._y * scalar); + } + + public static XVector Multiply(double scalar, XVector vector) + { + return new XVector(vector._x * scalar, vector._y * scalar); + } + + public static XVector operator /(XVector vector, double scalar) + { + return vector * (1.0 / scalar); + } + + public static XVector Divide(XVector vector, double scalar) + { + return vector * (1.0 / scalar); + } + + public static XVector operator *(XVector vector, XMatrix matrix) + { + return matrix.Transform(vector); + } + + public static XVector Multiply(XVector vector, XMatrix matrix) + { + return matrix.Transform(vector); + } + + public static double operator *(XVector vector1, XVector vector2) + { + return vector1._x * vector2._x + vector1._y * vector2._y; + } + + public static double Multiply(XVector vector1, XVector vector2) + { + return vector1._x * vector2._x + vector1._y * vector2._y; + } + + public static double Determinant(XVector vector1, XVector vector2) + { + return vector1._x * vector2._y - vector1._y * vector2._x; + } + + public static explicit operator XSize(XVector vector) + { + return new XSize(Math.Abs(vector._x), Math.Abs(vector._y)); + } + + public static explicit operator XPoint(XVector vector) + { + return new XPoint(vector._x, vector._y); + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return string.Format(CultureInfo.InvariantCulture, "vector=({0:" + format + "}, {1:" + format + "})", _x, _y); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/PathStart.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/PathStart.cs new file mode 100644 index 00000000..8525204c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/PathStart.cs @@ -0,0 +1,54 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Drawing +{ + /// + /// Indicates how to handle the first point of a path. + /// + internal enum PathStart + { + /// + /// Set the current position to the first point. + /// + MoveTo1st, + + /// + /// Draws a line to the first point. + /// + LineTo1st, + + /// + /// Ignores the first point. + /// + Ignore1st, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XColorSpace.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XColorSpace.cs new file mode 100644 index 00000000..837be30c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XColorSpace.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Currently not used. Only DeviceRGB is rendered in PDF. + /// + public enum XColorSpace + { + /// + /// Identifies the RGB color space. + /// + Rgb, + + /// + /// Identifies the CMYK color space. + /// + Cmyk, + + /// + /// Identifies the gray scale color space. + /// + GrayScale, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XCombineMode.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XCombineMode.cs new file mode 100644 index 00000000..3f9f9759 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XCombineMode.cs @@ -0,0 +1,67 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies how different clipping regions can be combined. + /// + public enum XCombineMode // Same values as System.Drawing.Drawing2D.CombineMode. + { + /// + /// One clipping region is replaced by another. + /// + Replace = 0, + + /// + /// Two clipping regions are combined by taking their intersection. + /// + Intersect = 1, + + /// + /// Not yet implemented in PDFsharp. + /// + Union = 2, + + /// + /// Not yet implemented in PDFsharp. + /// + Xor = 3, + + /// + /// Not yet implemented in PDFsharp. + /// + Exclude = 4, + + /// + /// Not yet implemented in PDFsharp. + /// + Complement = 5, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XDashStyle.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XDashStyle.cs new file mode 100644 index 00000000..71fef701 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XDashStyle.cs @@ -0,0 +1,67 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the style of dashed lines drawn with an XPen object. + /// + public enum XDashStyle // Same values as System.Drawing.Drawing2D.DashStyle. + { + /// + /// Specifies a solid line. + /// + Solid = 0, + + /// + /// Specifies a line consisting of dashes. + /// + Dash = 1, + + /// + /// Specifies a line consisting of dots. + /// + Dot = 2, + + /// + /// Specifies a line consisting of a repeating pattern of dash-dot. + /// + DashDot = 3, + + /// + /// Specifies a line consisting of a repeating pattern of dash-dot-dot. + /// + DashDotDot = 4, + + /// + /// Specifies a user-defined custom dash style. + /// + Custom = 5, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XFillMode.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XFillMode.cs new file mode 100644 index 00000000..ec34a29a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XFillMode.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies how the interior of a closed path is filled. + /// + public enum XFillMode // Same values as System.Drawing.FillMode. + { + /// + /// Specifies the alternate fill mode. Called the 'odd-even rule' in PDF terminology. + /// + Alternate = 0, + + /// + /// Specifies the winding fill mode. Called the 'nonzero winding number rule' in PDF terminology. + /// + Winding = 1, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XFontStyle.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XFontStyle.cs new file mode 100644 index 00000000..1902ad20 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XFontStyle.cs @@ -0,0 +1,74 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing +{ + /// + /// Specifies style information applied to text. + /// + [Flags] + public enum XFontStyle // Same values as System.Drawing.FontStyle. + { + /// + /// Normal text. + /// + Regular = XGdiFontStyle.Regular, + + /// + /// Bold text. + /// + Bold = XGdiFontStyle.Bold, + + /// + /// Italic text. + /// + Italic = XGdiFontStyle.Italic, + + /// + /// Bold and italic text. + /// + BoldItalic = XGdiFontStyle.BoldItalic, + + /// + /// Underlined text. + /// + Underline = XGdiFontStyle.Underline, + + /// + /// Text with a line through the middle. + /// + Strikeout = XGdiFontStyle.Strikeout, + + // Additional flags: + // BoldSimulation + // ItalicSimulation // It is not ObliqueSimulation, because oblique is what is what you get and this simulates italic. + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XGdiFontStyle.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGdiFontStyle.cs new file mode 100644 index 00000000..84154909 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGdiFontStyle.cs @@ -0,0 +1,76 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace PdfSharp.Drawing +{ + /// + /// Backward compatibility. + /// + [Flags] + internal enum XGdiFontStyle // Same values as System.Drawing.FontStyle. + { + // Must be identical to both: + // System.Drawing.FontStyle and + // PdfSharp.Drawing.FontStyle + + /// + /// Normal text. + /// + Regular = 0, + + /// + /// Bold text. + /// + Bold = 1, + + /// + /// Italic text. + /// + Italic = 2, + + /// + /// Bold and italic text. + /// + BoldItalic = 3, + + /// + /// Underlined text. + /// + Underline = 4, + + /// + /// Text with a line through the middle. + /// + Strikeout = 8, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicRenderTarget.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicRenderTarget.cs new file mode 100644 index 00000000..ba8a48f5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicRenderTarget.cs @@ -0,0 +1,63 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Drawing +{ + /// + /// Determines whether rendering based on GDI+ or WPF. + /// For internal use in hybrid build only only. + /// + enum XGraphicTargetContext + { + // NETFX_CORE_TODO + NONE = 0, + + /// + /// Rendering does not depent on a particular technology. + /// + CORE = 1, + + /// + /// Renders using GDI+. + /// + GDI = 2, + + /// + /// Renders using WPF (including Silverlight). + /// + WPF = 3, + + /// + /// Universal Windows Platform. + /// + UWP = 10, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPathItemType.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPathItemType.cs new file mode 100644 index 00000000..2de763b9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPathItemType.cs @@ -0,0 +1,48 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Type of the path data. + /// + internal enum XGraphicsPathItemType + { + Lines, + Beziers, + Curve, + Arc, + Rectangle, + RoundedRectangle, + Ellipse, + Polygon, + CloseFigure, + StartFigure, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPdfPageOptions.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPdfPageOptions.cs new file mode 100644 index 00000000..4503a1da --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsPdfPageOptions.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies how the content of an existing PDF page and new content is combined. + /// + public enum XGraphicsPdfPageOptions + { + /// + /// The new content is inserted behind the old content and any subsequent drawing in done above the existing graphic. + /// + Append, + + /// + /// The new content is inserted before the old content and any subsequent drawing in done beneath the existing graphic. + /// + Prepend, + + /// + /// The new content entirely replaces the old content and any subsequent drawing in done on a blank page. + /// + Replace, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsUnit.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsUnit.cs new file mode 100644 index 00000000..c2c00e2e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XGraphicsUnit.cs @@ -0,0 +1,62 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the unit of measure. + /// + public enum XGraphicsUnit // NOT the same values as System.Drawing.GraphicsUnit + { + /// + /// Specifies a printer's point (1/72 inch) as the unit of measure. + /// + Point = 0, // Must be 0 to let a new XUnit be 0 point. + + /// + /// Specifies the inch (2.54 cm) as the unit of measure. + /// + Inch = 1, + + /// + /// Specifies the millimeter as the unit of measure. + /// + Millimeter = 2, + + /// + /// Specifies the centimeter as the unit of measure. + /// + Centimeter = 3, + + /// + /// Specifies a presentation point (1/96 inch) as the unit of measure. + /// + Presentation = 4, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XKnownColor.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XKnownColor.cs new file mode 100644 index 00000000..6a18cd42 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XKnownColor.cs @@ -0,0 +1,461 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies all pre-defined colors. Used to identify the pre-defined colors and to + /// localize their names. + /// + public enum XKnownColor + { + /// A pre-defined color. + AliceBlue = 0, + + /// A pre-defined color. + AntiqueWhite = 1, + + /// A pre-defined color. + Aqua = 2, + + /// A pre-defined color. + Aquamarine = 3, + + /// A pre-defined color. + Azure = 4, + + /// A pre-defined color. + Beige = 5, + + /// A pre-defined color. + Bisque = 6, + + /// A pre-defined color. + Black = 7, + + /// A pre-defined color. + BlanchedAlmond = 8, + + /// A pre-defined color. + Blue = 9, + + /// A pre-defined color. + BlueViolet = 10, + + /// A pre-defined color. + Brown = 11, + + /// A pre-defined color. + BurlyWood = 12, + + /// A pre-defined color. + CadetBlue = 13, + + /// A pre-defined color. + Chartreuse = 14, + + /// A pre-defined color. + Chocolate = 15, + + /// A pre-defined color. + Coral = 16, + + /// A pre-defined color. + CornflowerBlue = 17, + + /// A pre-defined color. + Cornsilk = 18, + + /// A pre-defined color. + Crimson = 19, + + /// A pre-defined color. + Cyan = 20, + + /// A pre-defined color. + DarkBlue = 21, + + /// A pre-defined color. + DarkCyan = 22, + + /// A pre-defined color. + DarkGoldenrod = 23, + + /// A pre-defined color. + DarkGray = 24, + + /// A pre-defined color. + DarkGreen = 25, + + /// A pre-defined color. + DarkKhaki = 26, + + /// A pre-defined color. + DarkMagenta = 27, + + /// A pre-defined color. + DarkOliveGreen = 28, + + /// A pre-defined color. + DarkOrange = 29, + + /// A pre-defined color. + DarkOrchid = 30, + + /// A pre-defined color. + DarkRed = 31, + + /// A pre-defined color. + DarkSalmon = 32, + + /// A pre-defined color. + DarkSeaGreen = 33, + + /// A pre-defined color. + DarkSlateBlue = 34, + + /// A pre-defined color. + DarkSlateGray = 35, + + /// A pre-defined color. + DarkTurquoise = 36, + + /// A pre-defined color. + DarkViolet = 37, + + /// A pre-defined color. + DeepPink = 38, + + /// A pre-defined color. + DeepSkyBlue = 39, + + /// A pre-defined color. + DimGray = 40, + + /// A pre-defined color. + DodgerBlue = 41, + + /// A pre-defined color. + Firebrick = 42, + + /// A pre-defined color. + FloralWhite = 43, + + /// A pre-defined color. + ForestGreen = 44, + + /// A pre-defined color. + Fuchsia = 45, + + /// A pre-defined color. + Gainsboro = 46, + + /// A pre-defined color. + GhostWhite = 47, + + /// A pre-defined color. + Gold = 48, + + /// A pre-defined color. + Goldenrod = 49, + + /// A pre-defined color. + Gray = 50, + + /// A pre-defined color. + Green = 51, + + /// A pre-defined color. + GreenYellow = 52, + + /// A pre-defined color. + Honeydew = 53, + + /// A pre-defined color. + HotPink = 54, + + /// A pre-defined color. + IndianRed = 55, + + /// A pre-defined color. + Indigo = 56, + + /// A pre-defined color. + Ivory = 57, + + /// A pre-defined color. + Khaki = 58, + + /// A pre-defined color. + Lavender = 59, + + /// A pre-defined color. + LavenderBlush = 60, + + /// A pre-defined color. + LawnGreen = 61, + + /// A pre-defined color. + LemonChiffon = 62, + + /// A pre-defined color. + LightBlue = 63, + + /// A pre-defined color. + LightCoral = 64, + + /// A pre-defined color. + LightCyan = 65, + + /// A pre-defined color. + LightGoldenrodYellow = 66, + + /// A pre-defined color. + LightGray = 67, + + /// A pre-defined color. + LightGreen = 68, + + /// A pre-defined color. + LightPink = 69, + + /// A pre-defined color. + LightSalmon = 70, + + /// A pre-defined color. + LightSeaGreen = 71, + + /// A pre-defined color. + LightSkyBlue = 72, + + /// A pre-defined color. + LightSlateGray = 73, + + /// A pre-defined color. + LightSteelBlue = 74, + + /// A pre-defined color. + LightYellow = 75, + + /// A pre-defined color. + Lime = 76, + + /// A pre-defined color. + LimeGreen = 77, + + /// A pre-defined color. + Linen = 78, + + /// A pre-defined color. + Magenta = 79, + + /// A pre-defined color. + Maroon = 80, + + /// A pre-defined color. + MediumAquamarine = 81, + + /// A pre-defined color. + MediumBlue = 82, + + /// A pre-defined color. + MediumOrchid = 83, + + /// A pre-defined color. + MediumPurple = 84, + + /// A pre-defined color. + MediumSeaGreen = 85, + + /// A pre-defined color. + MediumSlateBlue = 86, + + /// A pre-defined color. + MediumSpringGreen = 87, + + /// A pre-defined color. + MediumTurquoise = 88, + + /// A pre-defined color. + MediumVioletRed = 89, + + /// A pre-defined color. + MidnightBlue = 90, + + /// A pre-defined color. + MintCream = 91, + + /// A pre-defined color. + MistyRose = 92, + + /// A pre-defined color. + Moccasin = 93, + + /// A pre-defined color. + NavajoWhite = 94, + + /// A pre-defined color. + Navy = 95, + + /// A pre-defined color. + OldLace = 96, + + /// A pre-defined color. + Olive = 97, + + /// A pre-defined color. + OliveDrab = 98, + + /// A pre-defined color. + Orange = 99, + + /// A pre-defined color. + OrangeRed = 100, + + /// A pre-defined color. + Orchid = 101, + + /// A pre-defined color. + PaleGoldenrod = 102, + + /// A pre-defined color. + PaleGreen = 103, + + /// A pre-defined color. + PaleTurquoise = 104, + + /// A pre-defined color. + PaleVioletRed = 105, + + /// A pre-defined color. + PapayaWhip = 106, + + /// A pre-defined color. + PeachPuff = 107, + + /// A pre-defined color. + Peru = 108, + + /// A pre-defined color. + Pink = 109, + + /// A pre-defined color. + Plum = 110, + + /// A pre-defined color. + PowderBlue = 111, + + /// A pre-defined color. + Purple = 112, + + /// A pre-defined color. + Red = 113, + + /// A pre-defined color. + RosyBrown = 114, + + /// A pre-defined color. + RoyalBlue = 115, + + /// A pre-defined color. + SaddleBrown = 116, + + /// A pre-defined color. + Salmon = 117, + + /// A pre-defined color. + SandyBrown = 118, + + /// A pre-defined color. + SeaGreen = 119, + + /// A pre-defined color. + SeaShell = 120, + + /// A pre-defined color. + Sienna = 121, + + /// A pre-defined color. + Silver = 122, + + /// A pre-defined color. + SkyBlue = 123, + + /// A pre-defined color. + SlateBlue = 124, + + /// A pre-defined color. + SlateGray = 125, + + /// A pre-defined color. + Snow = 126, + + /// A pre-defined color. + SpringGreen = 127, + + /// A pre-defined color. + SteelBlue = 128, + + /// A pre-defined color. + Tan = 129, + + /// A pre-defined color. + Teal = 130, + + /// A pre-defined color. + Thistle = 131, + + /// A pre-defined color. + Tomato = 132, + + /// A pre-defined color. + Transparent = 133, + + /// A pre-defined color. + Turquoise = 134, + + /// A pre-defined color. + Violet = 135, + + /// A pre-defined color. + Wheat = 136, + + /// A pre-defined color. + White = 137, + + /// A pre-defined color. + WhiteSmoke = 138, + + /// A pre-defined color. + Yellow = 139, + + /// A pre-defined color. + YellowGreen = 140, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineAlignment.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineAlignment.cs new file mode 100644 index 00000000..96470d7e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineAlignment.cs @@ -0,0 +1,62 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the alignment of a text string relative to its layout rectangle + /// + public enum XLineAlignment // same values as System.Drawing.StringAlignment (except BaseLine) + { + /// + /// Specifies the text be aligned near the layout. + /// In a left-to-right layout, the near position is left. In a right-to-left layout, the near + /// position is right. + /// + Near = 0, + + /// + /// Specifies that text is aligned in the center of the layout rectangle. + /// + Center = 1, + + /// + /// Specifies that text is aligned far from the origin position of the layout rectangle. + /// In a left-to-right layout, the far position is right. In a right-to-left layout, the far + /// position is left. + /// + Far = 2, + + /// + /// Specifies that text is aligned relative to its base line. + /// With this option the layout rectangle must have a height of 0. + /// + BaseLine = 3, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineCap.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineCap.cs new file mode 100644 index 00000000..6238445b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineCap.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the available cap styles with which an XPen object can start and end a line. + /// + public enum XLineCap + { + /// + /// Specifies a flat line cap. + /// + Flat = 0, + + /// + /// Specifies a round line cap. + /// + Round = 1, + + /// + /// Specifies a square line cap. + /// + Square = 2 + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineJoin.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineJoin.cs new file mode 100644 index 00000000..0f85eb15 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLineJoin.cs @@ -0,0 +1,53 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies how to join consecutive line or curve segments in a figure or subpath. + /// + public enum XLineJoin + { + /// + /// Specifies a mitered join. This produces a sharp corner or a clipped corner, + /// depending on whether the length of the miter exceeds the miter limit + /// + Miter = 0, + + /// + /// Specifies a circular join. This produces a smooth, circular arc between the lines. + /// + Round = 1, + + /// + /// Specifies a beveled join. This produces a diagonal corner. + /// + Bevel = 2, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XLinearGradientMode.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLinearGradientMode.cs new file mode 100644 index 00000000..cc215a0b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XLinearGradientMode.cs @@ -0,0 +1,57 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the direction of a linear gradient. + /// + public enum XLinearGradientMode // same values as System.Drawing.LinearGradientMode + { + /// + /// Specifies a gradient from left to right. + /// + Horizontal = 0, + + /// + /// Specifies a gradient from top to bottom. + /// + Vertical = 1, + + /// + /// Specifies a gradient from upper left to lower right. + /// + ForwardDiagonal = 2, + + /// + /// Specifies a gradient from upper right to lower left. + /// + BackwardDiagonal = 3, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XMatrixOrder.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XMatrixOrder.cs new file mode 100644 index 00000000..48bd5304 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XMatrixOrder.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the order for matrix transform operations. + /// + public enum XMatrixOrder + { + /// + /// The new operation is applied before the old operation. + /// + Prepend = 0, + + /// + /// The new operation is applied after the old operation. + /// + Append = 1, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XPageDirection.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XPageDirection.cs new file mode 100644 index 00000000..cf2f961a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XPageDirection.cs @@ -0,0 +1,51 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the direction of the y-axis. + /// + public enum XPageDirection + { + /// + /// Increasing Y values go downwards. This is the default. + /// + Downwards = 0, + + /// + /// Increasing Y values go upwards. This is only possible when drawing on a PDF page. + /// It is not implemented when drawing on a System.Drawing.Graphics object. + /// + [Obsolete("Not implemeted - yagni")] + Upwards = 1, // Possible, but needs a lot of case differentiation - postponed. + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XSmoothingMode.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XSmoothingMode.cs new file mode 100644 index 00000000..76551c1a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XSmoothingMode.cs @@ -0,0 +1,73 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing +{ + /// + /// Specifies whether smoothing (or antialiasing) is applied to lines and curves + /// and the edges of filled areas. + /// + [Flags] + public enum XSmoothingMode // same values as System.Drawing.Drawing2D.SmoothingMode + { + // Not used in PDF rendering process. + + /// + /// Specifies an invalid mode. + /// + Invalid = -1, + + /// + /// Specifies the default mode. + /// + Default = 0, + + /// + /// Specifies high speed, low quality rendering. + /// + HighSpeed = 1, + + /// + /// Specifies high quality, low speed rendering. + /// + HighQuality = 2, + + /// + /// Specifies no antialiasing. + /// + None = 3, + + /// + /// Specifies antialiased rendering. + /// + AntiAlias = 4, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XStringAlignment.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XStringAlignment.cs new file mode 100644 index 00000000..983f0791 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XStringAlignment.cs @@ -0,0 +1,56 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Specifies the alignment of a text string relative to its layout rectangle. + /// + public enum XStringAlignment // Same values as System.Drawing.StringAlignment. + { + /// + /// Specifies the text be aligned near the layout. + /// In a left-to-right layout, the near position is left. In a right-to-left layout, the near + /// position is right. + /// + Near = 0, + + /// + /// Specifies that text is aligned in the center of the layout rectangle. + /// + Center = 1, + + /// + /// Specifies that text is aligned far from the origin position of the layout rectangle. + /// In a left-to-right layout, the far position is right. In a right-to-left layout, the far + /// position is left. + /// + Far = 2, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XStyleSimulations.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XStyleSimulations.cs new file mode 100644 index 00000000..d8afb5dd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XStyleSimulations.cs @@ -0,0 +1,60 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Drawing +{ + /// + /// Describes the simulation style of a font. + /// + [Flags] + public enum XStyleSimulations // Identical to WpfStyleSimulations. + { + /// + /// No font style simulation. + /// + None = 0, + + /// + /// Bold style simulation. + /// + BoldSimulation = 1, + + /// + /// Italic style simulation. + /// + ItalicSimulation = 2, + + /// + /// Bold and Italic style simulation. + /// + BoldItalicSimulation = ItalicSimulation | BoldSimulation, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Drawing/enums/XSweepDirection.cs b/src/PDFsharp/src/PdfSharp/Drawing/enums/XSweepDirection.cs new file mode 100644 index 00000000..3358e522 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Drawing/enums/XSweepDirection.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Drawing +{ + /// + /// Defines the direction an elliptical arc is drawn. + /// + public enum XSweepDirection // Same values as System.Windows.Media.SweepDirection. + { + /// + /// Specifies that arcs are drawn in a counter clockwise (negative-angle) direction. + /// + Counterclockwise = 0, + + /// + /// Specifies that arcs are drawn in a clockwise (positive-angle) direction. + /// + Clockwise = 1, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Events/DocumentEvents.cs b/src/PDFsharp/src/PdfSharp/Events/DocumentEvents.cs new file mode 100644 index 00000000..631799c8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Events/DocumentEvents.cs @@ -0,0 +1,215 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; +using PdfSharp.Pdf; + +namespace PdfSharp.Events +{ + /// + /// Base class for EventArgs in PDFsharp. + /// + public abstract class PdfSharpEventArgs : EventArgs + { + /// + /// The source of the event. + /// + public PdfObject Source { get; set; } + } + + /// + /// The event type of a PageEvent. + /// + public enum PageEventType + { + /// + /// A new page was created. + /// + Created, + /// + /// A page was moved. + /// + Moved, + /// + /// A page was imported from another document. + /// + Imported, + /// + /// A page was removed. + /// + Removed + } + + /// + /// EventArgs for changes in the PdfPages of adocument. + /// + public class PageEventArgs : PdfSharpEventArgs + { + /// + /// Gets or sets the affected page. + /// + public PdfPage Page { get; set; } + + /// + /// Gets or sets the page index of the affected page. + /// + public int PageIndex { get; set; } + + /// + /// The event type of PageEvent. + /// + public PageEventType EventType { get; internal set; } + } + + /// + /// EventHandler for OnPageAdded and OnPageRemoved. + /// + /// The sender of the event. + /// The PageEventArgs of the event. + public delegate void PageAddedOrRemovedEventHandler(object sender, PageEventArgs e); + + /// + /// The action type of a PageGraphicsEvent. + /// + public enum PageGraphicsActionType + { + /// + /// The XGraphics object for the page was created. + /// + GraphicsCreated = 1, + /// + /// DrawString() was called on the page's XGraphics object. + /// + DrawString, + /// + /// Another method drawing content was called on the page's XGraphics object. + /// + Draw + } + + /// + /// EventArgs for actions on a page's XGraphics object. + /// + public class PageGraphicsEventArgs : PdfSharpEventArgs + { + /// + /// Gets the page xxxxx. + /// + public PdfPage Page { get; internal set; } + + /// + /// Gets the created XGraphics object. + /// + public XGraphics Graphics { get; internal set; } + + /// + /// The action type of PageGraphicsEvent. + /// + public PageGraphicsActionType ActionType { get; internal set; } + } + + /// + /// EventHandler for OnPageGraphicsAction. + /// + /// The sender of the event. + /// The PageGraphicsEventArgs of the event. + public delegate void PageGraphicsEventHandler(object sender, PageGraphicsEventArgs e); + + + /// + /// A class encapsulating all events of a PdfDocument. + /// + public class DocumentEvents + { + /// + /// An event raised if a page was added. + /// + /// The sender of the event. + /// The PageEventArgs of the event. + public void OnPageAdded(object sender, PageEventArgs args) + { + if (PageAdded != null) + PageAdded(sender, args); + } + + /// + /// EventHandler for OnPageAdded. + /// + public event PageAddedOrRemovedEventHandler PageAdded; + + /// + /// An event raised if a page was removes. + /// + /// The sender of the event. + /// The PageEventArgs of the event. + public void OnPageRemoved(object sender, PageEventArgs args) + { + if (PageRemoved != null) + PageRemoved(sender, args); + } + + /// + /// EventHandler for OnPageRemoved. + /// + public event PageAddedOrRemovedEventHandler PageRemoved; + + /// + /// An event raised if the XGraphics object of a page is created. + /// + /// The sender of the event. + /// The PageGraphicsEventArgs of the event. + public void OnPageGraphicsCreated(object sender, PageGraphicsEventArgs args) + { + if (PageGraphicsCreated != null) + PageGraphicsCreated(sender, args); + } + + /// + /// EventHandler for OnPageGraphicsCreated. + /// + public event PageGraphicsEventHandler PageGraphicsCreated; + + /// + /// An event raised if something is drawn on a page's XGraphics object. + /// + /// The sender of the event. + /// The PageGraphicsEventArgs of the event. + public void OnPageGraphicsAction(object sender, PageGraphicsEventArgs args) + { + if (PageGraphicsAction != null) + PageGraphicsAction(sender, args); + } + + /// + /// EventHandler for OnPageGraphicsAction. + /// + public event PageGraphicsEventHandler PageGraphicsAction; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/FontDescriptor.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/FontDescriptor.cs new file mode 100644 index 00000000..3b4ae4aa --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/FontDescriptor.cs @@ -0,0 +1,398 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif +using PdfSharp.Pdf.Internal; +using PdfSharp.Fonts; +#if !EDF_CORE +using PdfSharp.Drawing; +#endif + +#pragma warning disable 0649 + +namespace PdfSharp.Fonts.OpenType +{ + // TODO: Needs to be refactored #??? + /// + /// Base class for all font descriptors. + /// Currently only OpenTypeDescriptor is derived from this base class. + /// + internal class FontDescriptor + { + protected FontDescriptor(string key) + { + _key = key; + } + + public string Key + { + get { return _key; } + } + readonly string _key; + + + + + + + + ///// + ///// + ///// + //public string FontFile + //{ + // get { return _fontFile; } + // private set { _fontFile = value; } // BUG: never set + //} + //string _fontFile; + + ///// + ///// + ///// + //public string FontType + //{ + // get { return _fontType; } + // private set { _fontType = value; } // BUG: never set + //} + //string _fontType; + + /// + /// + /// + public string FontName + { + get { return _fontName; } + protected set { _fontName = value; } + } + string _fontName; + + ///// + ///// + ///// + //public string FullName + //{ + // get { return _fullName; } + // private set { _fullName = value; } // BUG: never set + //} + //string _fullName; + + ///// + ///// + ///// + //public string FamilyName + //{ + // get { return _familyName; } + // private set { _familyName = value; } // BUG: never set + //} + //string _familyName; + + /// + /// + /// + public string Weight + { + get { return _weight; } + private set { _weight = value; } // BUG: never set + } + string _weight; + + /// + /// Gets a value indicating whether this instance belongs to a bold font. + /// + public virtual bool IsBoldFace + { + get { return false; } + } + + /// + /// + /// + public float ItalicAngle + { + get { return _italicAngle; } + protected set { _italicAngle = value; } + } + float _italicAngle; + + /// + /// Gets a value indicating whether this instance belongs to an italic font. + /// + public virtual bool IsItalicFace + { + get { return false; } + } + + /// + /// + /// + public int XMin + { + get { return _xMin; } + protected set { _xMin = value; } + } + int _xMin; + + /// + /// + /// + public int YMin + { + get { return _yMin; } + protected set { _yMin = value; } + } + int _yMin; + + /// + /// + /// + public int XMax + { + get { return _xMax; } + protected set { _xMax = value; } + } + int _xMax; + + /// + /// + /// + public int YMax + { + get { return _yMax; } + protected set { _yMax = value; } + } + int _yMax; + + /// + /// + /// + public bool IsFixedPitch + { + get { return _isFixedPitch; } + private set { _isFixedPitch = value; } // BUG: never set + } + bool _isFixedPitch; + + /// + /// + /// + public int UnderlinePosition + { + get { return _underlinePosition; } + protected set { _underlinePosition = value; } + } + int _underlinePosition; + + /// + /// + /// + public int UnderlineThickness + { + get { return _underlineThickness; } + protected set { _underlineThickness = value; } + } + int _underlineThickness; + + /// + /// + /// + public int StrikeoutPosition + { + get { return _strikeoutPosition; } + protected set { _strikeoutPosition = value; } + } + int _strikeoutPosition; + + /// + /// + /// + public int StrikeoutSize + { + get { return _strikeoutSize; } + protected set { _strikeoutSize = value; } + } + int _strikeoutSize; + + /// + /// + /// + public string Version + { + get { return _version; } + private set { _version = value; } // BUG: never set + } + string _version; + + ///// + ///// + ///// + //public string Notice + //{ + // get { return Notice; } + //} + //protected string notice; + + /// + /// + /// + public string EncodingScheme + { + get { return _encodingScheme; } + private set { _encodingScheme = value; } // BUG: never set + } + string _encodingScheme; + + /// + /// + /// + public int UnitsPerEm + { + get { return _unitsPerEm; } + protected set { _unitsPerEm = value; } + } + int _unitsPerEm; + + /// + /// + /// + public int CapHeight + { + get { return _capHeight; } + protected set { _capHeight = value; } + } + int _capHeight; + + /// + /// + /// + public int XHeight + { + get { return _xHeight; } + protected set { _xHeight = value; } + } + int _xHeight; + + /// + /// + /// + public int Ascender + { + get { return _ascender; } + protected set { _ascender = value; } + } + int _ascender; + + /// + /// + /// + public int Descender + { + get { return _descender; } + protected set { _descender = value; } + } + int _descender; + + /// + /// + /// + public int Leading + { + get { return _leading; } + protected set { _leading = value; } + } + int _leading; + + /// + /// + /// + public int Flags + { + get { return _flags; } + private set { _flags = value; } // BUG: never set + } + int _flags; + + /// + /// + /// + public int StemV + { + get { return _stemV; } + protected set { _stemV = value; } + } + int _stemV; + + /// + /// + /// + public int LineSpacing + { + get { return _lineSpacing; } + protected set { _lineSpacing = value; } + } + int _lineSpacing; + + + internal static string ComputeKey(XFont font) + { + return font.GlyphTypeface.Key; + //return ComputeKey(font.GlyphTypeface.Fontface.FullFaceName, font.Style); + //XGlyphTypeface glyphTypeface = font.GlyphTypeface; + //string key = glyphTypeface.Fontface.FullFaceName.ToLowerInvariant() + + // (glyphTypeface.IsBold ? "/b" : "") + (glyphTypeface.IsItalic ? "/i" : ""); + //return key; + } + + internal static string ComputeKey(string name, XFontStyle style) + { + return ComputeKey(name, + (style & XFontStyle.Bold) == XFontStyle.Bold, + (style & XFontStyle.Italic) == XFontStyle.Italic); + } + + internal static string ComputeKey(string name, bool isBold, bool isItalic) + { + string key = name.ToLowerInvariant() + '/' + + (isBold ? "b" : "") + (isItalic ? "i" : ""); + return key; + } + + internal static string ComputeKey(string name) + { + string key = name.ToLowerInvariant(); + return key; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GenericFontTable.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GenericFontTable.cs new file mode 100644 index 00000000..d1fb7119 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GenericFontTable.cs @@ -0,0 +1,70 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +//using Fixed = System.Int32; +//using FWord = System.Int16; +//using UFWord = System.UInt16; + +namespace PdfSharp.Fonts.OpenType +{ +#if true_ + /// + /// Generic font table. Not yet used + /// + internal class GenericFontTable : OpenTypeFontTable + { + public GenericFontTable(OpenTypeFontTable fontTable) + : base(null, "xxxx") + { + DirectoryEntry.Tag = fontTable.DirectoryEntry.Tag; + int length = fontTable.DirectoryEntry.Length; + if (length > 0) + { + _table = new byte[length]; + Buffer.BlockCopy(fontTable.FontData.Data, fontTable.DirectoryEntry.Offset, _table, 0, length); + } + } + + public GenericFontTable(OpenTypeFontface fontData, string tag) + : base(fontData, tag) + { + _fontData = fontData; + } + + protected override OpenTypeFontTable DeepCopy() + { + GenericFontTable fontTable = (GenericFontTable)base.DeepCopy(); + fontTable._table = (byte[])_table.Clone(); + return fontTable; + } + + byte[] _table; + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphDataTable.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphDataTable.cs new file mode 100644 index 00000000..009dcbcd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphDataTable.cs @@ -0,0 +1,189 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define VERBOSE_ + +using System; +using System.Collections.Generic; + +//using Fixed = System.Int32; +//using FWord = System.Int16; +//using UFWord = System.UInt16; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// This table contains information that describes the glyphs in the font in the TrueType outline format. + /// Information regarding the rasterizer (scaler) refers to the TrueType rasterizer. + /// http://www.microsoft.com/typography/otspec/glyf.htm + /// + internal class GlyphDataTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Glyf; + + internal byte[] GlyphTable; + + public GlyphDataTable() + : base(null, Tag) + { + DirectoryEntry.Tag = TableTagNames.Glyf; + } + + public GlyphDataTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry.Tag = TableTagNames.Glyf; + Read(); + } + + /// + /// Converts the bytes in a handy representation + /// + public void Read() + { + try + { + // not yet needed... + } + catch (Exception ex) + { + throw ex; + } + } + + /// + /// Gets the data of the specified glyph. + /// + public byte[] GetGlyphData(int glyph) + { + IndexToLocationTable loca = _fontData.loca; + int start = GetOffset(glyph); + int next = GetOffset(glyph + 1); + int count = next - start; + byte[] bytes = new byte[count]; + Buffer.BlockCopy(_fontData.FontSource.Bytes, start, bytes, 0, count); + return bytes; + } + + /// + /// Gets the size of the byte array that defines the glyph. + /// + public int GetGlyphSize(int glyph) + { + IndexToLocationTable loca = _fontData.loca; + return GetOffset(glyph + 1) - GetOffset(glyph); + } + + /// + /// Gets the offset of the specified glyph relative to the first byte of the font image. + /// + public int GetOffset(int glyph) + { + return DirectoryEntry.Offset + _fontData.loca.LocaTable[glyph]; + } + + /// + /// Adds for all composite glyphs the glyphs the composite one is made of. + /// + public void CompleteGlyphClosure(Dictionary glyphs) + { + int count = glyphs.Count; + int[] glyphArray = new int[glyphs.Count]; + glyphs.Keys.CopyTo(glyphArray, 0); + if (!glyphs.ContainsKey(0)) + glyphs.Add(0, null); + for (int idx = 0; idx < count; idx++) + AddCompositeGlyphs(glyphs, glyphArray[idx]); + } + + /// + /// If the specified glyph is a composite glyph add the glyphs it is made of to the glyph table. + /// + void AddCompositeGlyphs(Dictionary glyphs, int glyph) + { + //int start = fontData.loca.GetOffset(glyph); + int start = GetOffset(glyph); + // Has no contour? + if (start == GetOffset(glyph + 1)) + return; + _fontData.Position = start; + int numContours = _fontData.ReadShort(); + // Is not a composite glyph? + if (numContours >= 0) + return; + _fontData.SeekOffset(8); + for (; ; ) + { + int flags = _fontData.ReadUFWord(); + int cGlyph = _fontData.ReadUFWord(); + if (!glyphs.ContainsKey(cGlyph)) + glyphs.Add(cGlyph, null); + if ((flags & MORE_COMPONENTS) == 0) + return; + int offset = (flags & ARG_1_AND_2_ARE_WORDS) == 0 ? 2 : 4; + if ((flags & WE_HAVE_A_SCALE) != 0) + offset += 2; + else if ((flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) + offset += 4; + if ((flags & WE_HAVE_A_TWO_BY_TWO) != 0) + offset += 8; + _fontData.SeekOffset(offset); + } + } + + /// + /// Prepares the font table to be compiled into its binary representation. + /// + public override void PrepareForCompilation() + { + base.PrepareForCompilation(); + + if (DirectoryEntry.Length == 0) + DirectoryEntry.Length = GlyphTable.Length; + DirectoryEntry.CheckSum = CalcChecksum(GlyphTable); + } + + /// + /// Converts the font into its binary representation. + /// + public override void Write(OpenTypeFontWriter writer) + { + writer.Write(GlyphTable, 0, DirectoryEntry.PaddedLength); + } + + // ReSharper disable InconsistentNaming + // Constants from OpenType spec. + const int ARG_1_AND_2_ARE_WORDS = 1; + const int WE_HAVE_A_SCALE = 8; + const int MORE_COMPONENTS = 32; + const int WE_HAVE_AN_X_AND_Y_SCALE = 64; + const int WE_HAVE_A_TWO_BY_TWO = 128; + // ReSharper restore InconsistentNaming + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs new file mode 100644 index 00000000..c716d3d4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/GlyphTypefaceCache.cs @@ -0,0 +1,116 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Text; +using PdfSharp.Drawing; +using PdfSharp.Internal; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Global table of all glyph typefaces. + /// + internal class GlyphTypefaceCache + { + GlyphTypefaceCache() + { + _glyphTypefacesByKey = new Dictionary(); + } + + public static bool TryGetGlyphTypeface(string key, out XGlyphTypeface glyphTypeface) + { + try + { + Lock.EnterFontFactory(); + bool result = Singleton._glyphTypefacesByKey.TryGetValue(key, out glyphTypeface); + return result; + } + finally { Lock.ExitFontFactory(); } + } + + public static void AddGlyphTypeface(XGlyphTypeface glyphTypeface) + { + try + { + Lock.EnterFontFactory(); + GlyphTypefaceCache cache = Singleton; + Debug.Assert(!cache._glyphTypefacesByKey.ContainsKey(glyphTypeface.Key)); + cache._glyphTypefacesByKey.Add(glyphTypeface.Key, glyphTypeface); + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Gets the singleton. + /// + static GlyphTypefaceCache Singleton + { + get + { + // ReSharper disable once InvertIf + if (_singleton == null) + { + try + { + Lock.EnterFontFactory(); + if (_singleton == null) + _singleton = new GlyphTypefaceCache(); + } + finally { Lock.ExitFontFactory(); } + } + return _singleton; + } + } + static volatile GlyphTypefaceCache _singleton; + + internal static string GetCacheState() + { + StringBuilder state = new StringBuilder(); + state.Append("====================\n"); + state.Append("Glyph typefaces by name\n"); + Dictionary.KeyCollection familyKeys = Singleton._glyphTypefacesByKey.Keys; + int count = familyKeys.Count; + string[] keys = new string[count]; + familyKeys.CopyTo(keys, 0); + Array.Sort(keys, StringComparer.OrdinalIgnoreCase); + foreach (string key in keys) + state.AppendFormat(" {0}: {1}\n", key, Singleton._glyphTypefacesByKey[key].DebuggerDisplay); + state.Append("\n"); + return state.ToString(); + } + + /// + /// Maps typeface key to glyph typeface. + /// + readonly Dictionary _glyphTypefacesByKey; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IRefFontTable.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IRefFontTable.cs new file mode 100644 index 00000000..2e570a20 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IRefFontTable.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using Fixed = System.Int32; +using FWord = System.Int16; +using UFWord = System.UInt16; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Represents an indirect reference to an existing font table in a font image. + /// Used to create binary copies of an existing font table that is not modified. + /// + // ReSharper disable once InconsistentNaming - "I" stands for "indirect", not "interface". + internal class IRefFontTable : OpenTypeFontTable + { + public IRefFontTable(OpenTypeFontface fontData, OpenTypeFontTable fontTable) + : base(null, fontTable.DirectoryEntry.Tag) + { + _fontData = fontData; + _irefDirectoryEntry = fontTable.DirectoryEntry; + } + + readonly TableDirectoryEntry _irefDirectoryEntry; + + /// + /// Prepares the font table to be compiled into its binary representation. + /// + public override void PrepareForCompilation() + { + base.PrepareForCompilation(); + DirectoryEntry.Length = _irefDirectoryEntry.Length; + DirectoryEntry.CheckSum = _irefDirectoryEntry.CheckSum; +#if DEBUG + // Check the checksum algorithm + if (DirectoryEntry.Tag != TableTagNames.Head) + { + byte[] bytes = new byte[DirectoryEntry.PaddedLength]; + Buffer.BlockCopy(_irefDirectoryEntry.FontTable._fontData.FontSource.Bytes, _irefDirectoryEntry.Offset, bytes, 0, DirectoryEntry.PaddedLength); + uint checkSum1 = DirectoryEntry.CheckSum; + uint checkSum2 = CalcChecksum(bytes); + // TODO: Sometimes this Assert fails, + //Debug.Assert(checkSum1 == checkSum2, "Bug in checksum algorithm."); + } +#endif + } + + /// + /// Converts the font into its binary representation. + /// + public override void Write(OpenTypeFontWriter writer) + { + writer.Write(_irefDirectoryEntry.FontTable._fontData.FontSource.Bytes, _irefDirectoryEntry.Offset, _irefDirectoryEntry.PaddedLength); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IndexToLocationTable.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IndexToLocationTable.cs new file mode 100644 index 00000000..f152230c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/IndexToLocationTable.cs @@ -0,0 +1,148 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define VERBOSE_ + +using System; +using System.Diagnostics; + +//using Fixed = System.Int32; +//using FWord = System.Int16; +//using UFWord = System.UInt16; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// The indexToLoc table stores the offsets to the locations of the glyphs in the font, + /// relative to the beginning of the glyphData table. In order to compute the length of + /// the last glyph element, there is an extra entry after the last valid index. + /// + internal class IndexToLocationTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Loca; + + internal int[] LocaTable; + + public IndexToLocationTable() + : base(null, Tag) + { + DirectoryEntry.Tag = TableTagNames.Loca; + } + + public IndexToLocationTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry = _fontData.TableDictionary[TableTagNames.Loca]; + Read(); + } + + public bool ShortIndex; + + /// + /// Converts the bytes in a handy representation + /// + public void Read() + { + try + { + ShortIndex = _fontData.head.indexToLocFormat == 0; + _fontData.Position = DirectoryEntry.Offset; + if (ShortIndex) + { + int entries = DirectoryEntry.Length / 2; + Debug.Assert(_fontData.maxp.numGlyphs + 1 == entries, + "For your information only: Number of glyphs mismatch in font. You can ignore this assertion."); + LocaTable = new int[entries]; + for (int idx = 0; idx < entries; idx++) + LocaTable[idx] = 2 * _fontData.ReadUFWord(); + } + else + { + int entries = DirectoryEntry.Length / 4; + Debug.Assert(_fontData.maxp.numGlyphs + 1 == entries, + "For your information only: Number of glyphs mismatch in font. You can ignore this assertion."); + LocaTable = new int[entries]; + for (int idx = 0; idx < entries; idx++) + LocaTable[idx] = _fontData.ReadLong(); + } + } + catch (Exception) + { + GetType(); + throw; + } + } + + /// + /// Prepares the font table to be compiled into its binary representation. + /// + public override void PrepareForCompilation() + { + DirectoryEntry.Offset = 0; + if (ShortIndex) + DirectoryEntry.Length = LocaTable.Length * 2; + else + DirectoryEntry.Length = LocaTable.Length * 4; + + _bytes = new byte[DirectoryEntry.PaddedLength]; + int length = LocaTable.Length; + int byteIdx = 0; + if (ShortIndex) + { + for (int idx = 0; idx < length; idx++) + { + int value = LocaTable[idx] / 2; + _bytes[byteIdx++] = (byte)(value >> 8); + _bytes[byteIdx++] = (byte)(value); + } + } + else + { + for (int idx = 0; idx < length; idx++) + { + int value = LocaTable[idx]; + _bytes[byteIdx++] = (byte)(value >> 24); + _bytes[byteIdx++] = (byte)(value >> 16); + _bytes[byteIdx++] = (byte)(value >> 8); + _bytes[byteIdx++] = (byte)value; + } + } + DirectoryEntry.CheckSum = CalcChecksum(_bytes); + } + byte[] _bytes; + + /// + /// Converts the font into its binary representation. + /// + public override void Write(OpenTypeFontWriter writer) + { + writer.Write(_bytes, 0, DirectoryEntry.PaddedLength); + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeDescriptor.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeDescriptor.cs new file mode 100644 index 00000000..30512bc8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeDescriptor.cs @@ -0,0 +1,425 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Text; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +#endif +using PdfSharp.Pdf.Internal; +#if !EDF_CORE +using PdfSharp.Drawing; +#endif + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// The OpenType font descriptor. + /// Currently the only font type PDFsharp supports. + /// + internal sealed class OpenTypeDescriptor : FontDescriptor + { + /// + /// New... + /// + public OpenTypeDescriptor(string fontDescriptorKey, string name, XFontStyle stlye, OpenTypeFontface fontface, XPdfFontOptions options) + : base(fontDescriptorKey) + { + FontFace = fontface; + FontName = name; + Initialize(); + } + + public OpenTypeDescriptor(string fontDescriptorKey, XFont font) + : base(fontDescriptorKey) + { + try + { + FontFace = font.GlyphTypeface.Fontface; + FontName = font.Name; + Initialize(); + } + catch + { + GetType(); + throw; + } + } + + internal OpenTypeDescriptor(string fontDescriptorKey, string idName, byte[] fontData) + : base(fontDescriptorKey) + { + try + { + FontFace = new OpenTypeFontface(fontData, idName); + // Try to get real name form name table + if (idName.Contains("XPS-Font-") && FontFace.name != null && FontFace.name.Name.Length != 0) + { + string tag = String.Empty; + if (idName.IndexOf('+') == 6) + tag = idName.Substring(0, 6); + idName = tag + "+" + FontFace.name.Name; + if (FontFace.name.Style.Length != 0) + idName += "," + FontFace.name.Style; + //idName = idName.Replace(" ", ""); + } + FontName = idName; + Initialize(); + } + catch (Exception) + { + GetType(); + throw; + } + } + + internal OpenTypeFontface FontFace; + + void Initialize() + { + // TODO: Respect embedding restrictions. + //bool embeddingRestricted = fontData.os2.fsType == 0x0002; + + //fontName = image.n + ItalicAngle = FontFace.post.italicAngle; + + XMin = FontFace.head.xMin; + YMin = FontFace.head.yMin; + XMax = FontFace.head.xMax; + YMax = FontFace.head.yMax; + + UnderlinePosition = FontFace.post.underlinePosition; + UnderlineThickness = FontFace.post.underlineThickness; + + // PDFlib states that some Apple fonts miss the OS/2 table. + Debug.Assert(FontFace.os2 != null, "TrueType font has no OS/2 table."); + + StrikeoutPosition = FontFace.os2.yStrikeoutPosition; + StrikeoutSize = FontFace.os2.yStrikeoutSize; + + // No documentation found how to get the set vertical stems width from the + // TrueType tables. + // The following formula comes from PDFlib Lite source code. Acrobat 5.0 sets + // /StemV to 0 always. I think the value doesn't matter. + //float weight = (float)(image.os2.usWeightClass / 65.0f); + //stemV = (int)(50 + weight * weight); // MAGIC + StemV = 0; + + UnitsPerEm = FontFace.head.unitsPerEm; + + // Calculate Ascent, Descent, Leading and LineSpacing like in WPF Source Code (see FontDriver.ReadBasicMetrics) + + // OS/2 is an optional table, but we can't determine if it is existing in this font. + bool os2SeemsToBeEmpty = FontFace.os2.sTypoAscender == 0 && FontFace.os2.sTypoDescender == 0 && FontFace.os2.sTypoLineGap == 0; + //Debug.Assert(!os2SeemsToBeEmpty); // Are there fonts without OS/2 table? + + bool dontUseWinLineMetrics = (FontFace.os2.fsSelection & 128) != 0; + if (!os2SeemsToBeEmpty && dontUseWinLineMetrics) + { + // Comment from WPF: The font specifies that the sTypoAscender, sTypoDescender, and sTypoLineGap fields are valid and + // should be used instead of winAscent and winDescent. + int typoAscender = FontFace.os2.sTypoAscender; + int typoDescender = FontFace.os2.sTypoDescender; + int typoLineGap = FontFace.os2.sTypoLineGap; + + // Comment from WPF: We include the line gap in the ascent so that white space is distributed above the line. (Note that + // the typo line gap is a different concept than "external leading".) + Ascender = typoAscender + typoLineGap; + // Comment from WPF: Typo descent is a signed value where the positive direction is up. It is therefore typically negative. + // A signed typo descent would be quite unusual as it would indicate the descender was above the baseline + Descender = -typoDescender; + LineSpacing = typoAscender + typoLineGap - typoDescender; + } + else + { + // Comment from WPF: get the ascender field + int ascender = FontFace.hhea.ascender; + // Comment from WPF: get the descender field; this is measured in the same direction as ascender and is therefore + // normally negative whereas we want a positive value; however some fonts get the sign wrong + // so instead of just negating we take the absolute value. + int descender = Math.Abs(FontFace.hhea.descender); + // Comment from WPF: get the lineGap field and make sure it's >= 0 + int lineGap = Math.Max((short)0, FontFace.hhea.lineGap); + + if (!os2SeemsToBeEmpty) + { + // Comment from WPF: we could use sTypoAscender, sTypoDescender, and sTypoLineGap which are supposed to represent + // optimal typographic values not constrained by backwards compatibility; however, many fonts get + // these fields wrong or get them right only for Latin text; therefore we use the more reliable + // platform-specific Windows values. We take the absolute value of the win32descent in case some + // fonts get the sign wrong. + int winAscent = FontFace.os2.usWinAscent; + int winDescent = Math.Abs(FontFace.os2.usWinDescent); + + Ascender = winAscent; + Descender = winDescent; + // Comment from WPF: The following calculation for designLineSpacing is per [....]. The default line spacing + // should be the sum of the Mac ascender, descender, and lineGap unless the resulting value would + // be less than the cell height (winAscent + winDescent) in which case we use the cell height. + // See also http://www.microsoft.com/typography/otspec/recom.htm. + // Note that in theory it's valid for the baseline-to-baseline distance to be less than the cell + // height. However, Windows has never allowed this for Truetype fonts, and fonts built for Windows + // sometimes rely on this behavior and get the hha values wrong or set them all to zero. + LineSpacing = Math.Max(lineGap + ascender + descender, winAscent + winDescent); + } + else + { + Ascender = ascender; + Descender = descender; + LineSpacing = ascender + descender + lineGap; + } + } + + Debug.Assert(Descender >= 0); + + int cellHeight = Ascender + Descender; + int internalLeading = cellHeight - UnitsPerEm; // Not used, only for debugging. + int externalLeading = LineSpacing - cellHeight; + Leading = externalLeading; + + // sCapHeight and sxHeight are only valid if Version >= 2 + if (FontFace.os2.version >= 2 && FontFace.os2.sCapHeight != 0) + CapHeight = FontFace.os2.sCapHeight; + else + CapHeight = Ascender; + + if (FontFace.os2.version >= 2 && FontFace.os2.sxHeight != 0) + XHeight = FontFace.os2.sxHeight; + else + XHeight = (int)(0.66 * Ascender); + + //flags = image. + +#if !EDF_CORE + Encoding ansi = PdfEncoders.WinAnsiEncoding; // System.Text.Encoding.Default; +#else + Encoding ansi = null; //$$$ PdfEncoders.WinAnsiEncoding; // System.Text.Encoding.Default; +#endif + + Encoding unicode = Encoding.Unicode; + byte[] bytes = new byte[256]; + + bool symbol = FontFace.cmap.symbol; + Widths = new int[256]; + for (int idx = 0; idx < 256; idx++) + { + bytes[idx] = (byte)idx; + // PDFlib handles some font flaws here... + // We wait for bug reports. + + char ch = (char)idx; + string s = ansi.GetString(bytes, idx, 1); + if (s.Length != 0) + { + if (s[0] != ch) + ch = s[0]; + } + + //Debug.Assert(ch == idx); + + //int glyphIndex; + //if (symbol) + //{ + // glyphIndex = idx + (FontFace.os2. usFirstCharIndex & 0xFF00); + // glyphIndex = CharCodeToGlyphIndex((char)glyphIndex); + //} + //else + //{ + // //Debug.Assert(idx + (fontData.os2.usFirstCharIndex & 0xFF00) == idx); + // //glyphIndex = CharCodeToGlyphIndex((char)idx); + // glyphIndex = CharCodeToGlyphIndex(ch); + //} + + if (symbol) + { + // Remap ch for symbol fonts. + ch = (char)(ch | (FontFace.os2.usFirstCharIndex & 0xFF00)); // @@@ refactor + } + int glyphIndex = CharCodeToGlyphIndex(ch); + Widths[idx] = GlyphIndexToPdfWidth(glyphIndex); + } + } + public int[] Widths; + + /// + /// Gets a value indicating whether this instance belongs to a bold font. + /// + public override bool IsBoldFace + { + get + { + // usWeightClass 700 is Bold + //Debug.Assert((fontData.os2.usWeightClass >= 700) == ((fontData.os2.fsSelection & (ushort)OS2Table.FontSelectionFlags.Bold) != 0)); + return FontFace.os2.IsBold; + } + } + + /// + /// Gets a value indicating whether this instance belongs to an italic font. + /// + public override bool IsItalicFace + { + get { return FontFace.os2.IsItalic; } + } + + internal int DesignUnitsToPdf(double value) + { + return (int)Math.Round(value * 1000.0 / FontFace.head.unitsPerEm); + } + + /// + /// Maps a unicode to the index of the corresponding glyph. + /// See OpenType spec "cmap - Character To Glyph Index Mapping Table / Format 4: Segment mapping to delta values" + /// for details about this a little bit strange looking algorithm. + /// + public int CharCodeToGlyphIndex(char value) + { + try + { + CMap4 cmap = FontFace.cmap.cmap4; + int segCount = cmap.segCountX2 / 2; + int seg; + for (seg = 0; seg < segCount; seg++) + { + if (value <= cmap.endCount[seg]) + break; + } + Debug.Assert(seg < segCount); + + if (value < cmap.startCount[seg]) + return 0; + + if (cmap.idRangeOffs[seg] == 0) + return (value + cmap.idDelta[seg]) & 0xFFFF; + + int idx = cmap.idRangeOffs[seg] / 2 + (value - cmap.startCount[seg]) - (segCount - seg); + Debug.Assert(idx >= 0 && idx < cmap.glyphCount); + + if (cmap.glyphIdArray[idx] == 0) + return 0; + + return (cmap.glyphIdArray[idx] + cmap.idDelta[seg]) & 0xFFFF; + } + catch + { + GetType(); + throw; + } + } + + /// + /// Converts the width of a glyph identified by its index to PDF design units. + /// + public int GlyphIndexToPdfWidth(int glyphIndex) + { + try + { + int numberOfHMetrics = FontFace.hhea.numberOfHMetrics; + int unitsPerEm = FontFace.head.unitsPerEm; + + // glyphIndex >= numberOfHMetrics means the font is mono-spaced and all glyphs have the same width + if (glyphIndex >= numberOfHMetrics) + glyphIndex = numberOfHMetrics - 1; + + int width = FontFace.hmtx.Metrics[glyphIndex].advanceWidth; + + // Sometimes the unitsPerEm is 1000, sometimes a power of 2. + if (unitsPerEm == 1000) + return width; + return width * 1000 / unitsPerEm; // normalize + } + catch (Exception) + { + GetType(); + throw; + } + } + + public int PdfWidthFromCharCode(char ch) + { + int idx = CharCodeToGlyphIndex(ch); + int width = GlyphIndexToPdfWidth(idx); + return width; + } + + /// + /// //Converts the width of a glyph identified by its index to PDF design units. + /// + public double GlyphIndexToEmfWidth(int glyphIndex, double emSize) + { + try + { + int numberOfHMetrics = FontFace.hhea.numberOfHMetrics; + int unitsPerEm = FontFace.head.unitsPerEm; + + // glyphIndex >= numberOfHMetrics means the font is mono-spaced and all glyphs have the same width + if (glyphIndex >= numberOfHMetrics) + glyphIndex = numberOfHMetrics - 1; + + int width = FontFace.hmtx.Metrics[glyphIndex].advanceWidth; + + return width * emSize / unitsPerEm; // normalize + } + catch (Exception) + { + GetType(); + throw; + } + } + + /// + /// //Converts the width of a glyph identified by its index to PDF design units. + /// + public int GlyphIndexToWidth(int glyphIndex) + { + try + { + int numberOfHMetrics = FontFace.hhea.numberOfHMetrics; + + // glyphIndex >= numberOfHMetrics means the font is mono-spaced and all glyphs have the same width + if (glyphIndex >= numberOfHMetrics) + glyphIndex = numberOfHMetrics - 1; + + int width = FontFace.hmtx.Metrics[glyphIndex].advanceWidth; + return width; + } + catch (Exception) + { + GetType(); + throw; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTable.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTable.cs new file mode 100644 index 00000000..c1e87510 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTable.cs @@ -0,0 +1,118 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; + +//using Fixed = System.Int32; +//using FWord = System.Int16; +//using UFWord = System.UInt16; +#if SILVERLIGHT +using PdfSharp; +#endif + +namespace PdfSharp.Fonts.OpenType +{ + // TODO: Create a font driver for reading and writing OpenType font files. + + /// + /// Base class for all OpenType tables used in PDFsharp. + /// + internal class OpenTypeFontTable : ICloneable + { + public OpenTypeFontTable(OpenTypeFontface fontData, string tag) + { + _fontData = fontData; + if (fontData != null && fontData.TableDictionary.ContainsKey(tag)) + DirectoryEntry = fontData.TableDictionary[tag]; + else + DirectoryEntry = new TableDirectoryEntry(tag); + DirectoryEntry.FontTable = this; + } + + /// + /// Creates a deep copy of the current instance. + /// + public object Clone() + { + return DeepCopy(); + } + + protected virtual OpenTypeFontTable DeepCopy() + { + OpenTypeFontTable fontTable = (OpenTypeFontTable)MemberwiseClone(); + fontTable.DirectoryEntry.Offset = 0; + fontTable.DirectoryEntry.FontTable = fontTable; + return fontTable; + } + + /// + /// Gets the font image the table belongs to. + /// + public OpenTypeFontface FontData + { + get { return _fontData; } + } + internal OpenTypeFontface _fontData; + + public TableDirectoryEntry DirectoryEntry; + + /// + /// When overridden in a derived class, prepares the font table to be compiled into its binary representation. + /// + public virtual void PrepareForCompilation() + { } + + /// + /// When overridden in a derived class, converts the font into its binary representation. + /// + public virtual void Write(OpenTypeFontWriter writer) + { } + + /// + /// Calculates the checksum of a table represented by its bytes. + /// + public static uint CalcChecksum(byte[] bytes) + { + Debug.Assert((bytes.Length & 3) == 0); + // Cannot use Buffer.BlockCopy because 32-bit values are Big-endian in fonts. + uint byte3, byte2, byte1, byte0; + byte3 = byte2 = byte1 = byte0 = 0; + int length = bytes.Length; + for (int idx = 0; idx < length;) + { + byte3 += bytes[idx++]; + byte2 += bytes[idx++]; + byte1 += bytes[idx++]; + byte0 += bytes[idx++]; + } + return (byte3 << 24) + (byte2 << 16) + (byte1 << 8) + byte0; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTables.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTables.cs new file mode 100644 index 00000000..2bb61e4f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontTables.cs @@ -0,0 +1,1050 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define VERBOSE_ + +using System; +using System.Diagnostics; +using System.Text; + +using Fixed = System.Int32; +using FWord = System.Int16; +using UFWord = System.UInt16; + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Fonts.OpenType +{ + internal enum PlatformId + { + Apple, Mac, Iso, Win + } + + /// + /// Only Symbol and Unicode is used by PDFsharp. + /// + internal enum WinEncodingId + { + Symbol, Unicode + } + + /// + /// CMap format 4: Segment mapping to delta values. + /// The Windows standard format. + /// + internal class CMap4 : OpenTypeFontTable + { + public WinEncodingId encodingId; // Windows encoding ID. + public ushort format; // Format number is set to 4. + public ushort length; // This is the length in bytes of the subtable. + public ushort language; // This field must be set to zero for all cmap subtables whose platform IDs are other than Macintosh (platform ID 1). + public ushort segCountX2; // 2 x segCount. + public ushort searchRange; // 2 x (2**floor(log2(segCount))) + public ushort entrySelector; // log2(searchRange/2) + public ushort rangeShift; + public ushort[] endCount; // [segCount] / End characterCode for each segment, last=0xFFFF. + public ushort[] startCount; // [segCount] / Start character code for each segment. + public short[] idDelta; // [segCount] / Delta for all character codes in segment. + public ushort[] idRangeOffs; // [segCount] / Offsets into glyphIdArray or 0 + public int glyphCount; // = (length - (16 + 4 * 2 * segCount)) / 2; + public ushort[] glyphIdArray; // Glyph index array (arbitrary length) + + public CMap4(OpenTypeFontface fontData, WinEncodingId encodingId) + : base(fontData, "----") + { + this.encodingId = encodingId; + Read(); + } + + internal void Read() + { + try + { + // m_EncodingID = encID; + format = _fontData.ReadUShort(); + Debug.Assert(format == 4, "Only format 4 expected."); + length = _fontData.ReadUShort(); + language = _fontData.ReadUShort(); // Always null in Windows + segCountX2 = _fontData.ReadUShort(); + searchRange = _fontData.ReadUShort(); + entrySelector = _fontData.ReadUShort(); + rangeShift = _fontData.ReadUShort(); + + int segCount = segCountX2 / 2; + glyphCount = (length - (16 + 8 * segCount)) / 2; + + //ASSERT_CONDITION(0 <= m_NumGlyphIds && m_NumGlyphIds < m_Length, "Invalid Index"); + + endCount = new ushort[segCount]; + startCount = new ushort[segCount]; + idDelta = new short[segCount]; + idRangeOffs = new ushort[segCount]; + + glyphIdArray = new ushort[glyphCount]; + + for (int idx = 0; idx < segCount; idx++) + endCount[idx] = _fontData.ReadUShort(); + + //ASSERT_CONDITION(m_EndCount[segs - 1] == 0xFFFF, "Out of Index"); + + // Read reserved pad. + _fontData.ReadUShort(); + + for (int idx = 0; idx < segCount; idx++) + startCount[idx] = _fontData.ReadUShort(); + + for (int idx = 0; idx < segCount; idx++) + idDelta[idx] = _fontData.ReadShort(); + + for (int idx = 0; idx < segCount; idx++) + idRangeOffs[idx] = _fontData.ReadUShort(); + + for (int idx = 0; idx < glyphCount; idx++) + glyphIdArray[idx] = _fontData.ReadUShort(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table defines the mapping of character codes to the glyph index values used in the font. + /// It may contain more than one subtable, in order to support more than one character encoding scheme. + /// + internal class CMapTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.CMap; + + public ushort version; + public ushort numTables; + + /// + /// Is true for symbol font encoding. + /// + public bool symbol; + + public CMap4 cmap4; + + /// + /// Initializes a new instance of the class. + /// + public CMapTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + internal void Read() + { + try + { + int tableOffset = _fontData.Position; + + version = _fontData.ReadUShort(); + numTables = _fontData.ReadUShort(); +#if DEBUG_ + if (_fontData.Name == "Cambria") + Debug-Break.Break(); +#endif + + bool success = false; + for (int idx = 0; idx < numTables; idx++) + { + PlatformId platformId = (PlatformId)_fontData.ReadUShort(); + WinEncodingId encodingId = (WinEncodingId)_fontData.ReadUShort(); + int offset = _fontData.ReadLong(); + + int currentPosition = _fontData.Position; + + // Just read Windows stuff. + if (platformId == PlatformId.Win && (encodingId == WinEncodingId.Symbol || encodingId == WinEncodingId.Unicode)) + { + symbol = encodingId == WinEncodingId.Symbol; + + _fontData.Position = tableOffset + offset; + cmap4 = new CMap4(_fontData, encodingId); + _fontData.Position = currentPosition; + // We have found what we are looking for, so break. + success = true; + break; + } + } + if (!success) + throw new InvalidOperationException("Font has no usable platform or encoding ID. It cannot be used with PDFsharp."); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table gives global information about the font. The bounding box values should be computed using + /// only glyphs that have contours. Glyphs with no contours should be ignored for the purposes of these calculations. + /// + internal class FontHeaderTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Head; + + public Fixed version; // 0x00010000 for Version 1.0. + public Fixed fontRevision; + public uint checkSumAdjustment; + public uint magicNumber; // Set to 0x5F0F3CF5 + public ushort flags; + public ushort unitsPerEm; // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines. + public long created; + public long modified; + public short xMin, yMin; // For all glyph bounding boxes. + public short xMax, yMax; // For all glyph bounding boxes. + public ushort macStyle; + public ushort lowestRecPPEM; + public short fontDirectionHint; + public short indexToLocFormat; // 0 for short offsets, 1 for long + public short glyphDataFormat; // 0 for current format + + public FontHeaderTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + version = _fontData.ReadFixed(); + fontRevision = _fontData.ReadFixed(); + checkSumAdjustment = _fontData.ReadULong(); + magicNumber = _fontData.ReadULong(); + flags = _fontData.ReadUShort(); + unitsPerEm = _fontData.ReadUShort(); + created = _fontData.ReadLongDate(); + modified = _fontData.ReadLongDate(); + xMin = _fontData.ReadShort(); + yMin = _fontData.ReadShort(); + xMax = _fontData.ReadShort(); + yMax = _fontData.ReadShort(); + macStyle = _fontData.ReadUShort(); + lowestRecPPEM = _fontData.ReadUShort(); + fontDirectionHint = _fontData.ReadShort(); + indexToLocFormat = _fontData.ReadShort(); + glyphDataFormat = _fontData.ReadShort(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table contains information for horizontal layout. The values in the minRightSidebearing, + /// MinLeftSideBearing and xMaxExtent should be computed using only glyphs that have contours. + /// Glyphs with no contours should be ignored for the purposes of these calculations. + /// All reserved areas must be set to 0. + /// + internal class HorizontalHeaderTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.HHea; + + public Fixed version; // 0x00010000 for Version 1.0. + public FWord ascender; // Typographic ascent. (Distance from baseline of highest Ascender) + public FWord descender; // Typographic descent. (Distance from baseline of lowest Descender) + public FWord lineGap; // Typographic line gap. Negative LineGap values are treated as zero in Windows 3.1, System 6, and System 7. + public UFWord advanceWidthMax; + public FWord minLeftSideBearing; + public FWord minRightSideBearing; + public FWord xMaxExtent; + public short caretSlopeRise; + public short caretSlopeRun; + public short reserved1; + public short reserved2; + public short reserved3; + public short reserved4; + public short reserved5; + public short metricDataFormat; + public ushort numberOfHMetrics; + + public HorizontalHeaderTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + version = _fontData.ReadFixed(); + ascender = _fontData.ReadFWord(); + descender = _fontData.ReadFWord(); + lineGap = _fontData.ReadFWord(); + advanceWidthMax = _fontData.ReadUFWord(); + minLeftSideBearing = _fontData.ReadFWord(); + minRightSideBearing = _fontData.ReadFWord(); + xMaxExtent = _fontData.ReadFWord(); + caretSlopeRise = _fontData.ReadShort(); + caretSlopeRun = _fontData.ReadShort(); + reserved1 = _fontData.ReadShort(); + reserved2 = _fontData.ReadShort(); + reserved3 = _fontData.ReadShort(); + reserved4 = _fontData.ReadShort(); + reserved5 = _fontData.ReadShort(); + metricDataFormat = _fontData.ReadShort(); + numberOfHMetrics = _fontData.ReadUShort(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + internal class HorizontalMetrics : OpenTypeFontTable + { + public const string Tag = "----"; + + public ushort advanceWidth; + public short lsb; + + public HorizontalMetrics(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + advanceWidth = _fontData.ReadUFWord(); + lsb = _fontData.ReadFWord(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// The type longHorMetric is defined as an array where each element has two parts: + /// the advance width, which is of type USHORT, and the left side bearing, which is of type SHORT. + /// These fields are in font design units. + /// + internal class HorizontalMetricsTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.HMtx; + + public HorizontalMetrics[] Metrics; + public FWord[] LeftSideBearing; + + public HorizontalMetricsTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + HorizontalHeaderTable hhea = _fontData.hhea; + MaximumProfileTable maxp = _fontData.maxp; + if (hhea != null && maxp != null) + { + int numMetrics = hhea.numberOfHMetrics; //->NumberOfHMetrics(); + int numLsbs = maxp.numGlyphs - numMetrics; + + Debug.Assert(numMetrics != 0); + Debug.Assert(numLsbs >= 0); + + Metrics = new HorizontalMetrics[numMetrics]; + for (int idx = 0; idx < numMetrics; idx++) + Metrics[idx] = new HorizontalMetrics(_fontData); + + if (numLsbs > 0) + { + LeftSideBearing = new FWord[numLsbs]; + for (int idx = 0; idx < numLsbs; idx++) + LeftSideBearing[idx] = _fontData.ReadFWord(); + } + } + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + // UNDONE + internal class VerticalHeaderTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.VHea; + + // code comes from HorizontalHeaderTable + public Fixed Version; // 0x00010000 for Version 1.0. + public FWord Ascender; // Typographic ascent. (Distance from baseline of highest Ascender) + public FWord Descender; // Typographic descent. (Distance from baseline of lowest Descender) + public FWord LineGap; // Typographic line gap. Negative LineGap values are treated as zero in Windows 3.1, System 6, and System 7. + public UFWord AdvanceWidthMax; + public FWord MinLeftSideBearing; + public FWord MinRightSideBearing; + public FWord xMaxExtent; + public short caretSlopeRise; + public short caretSlopeRun; + public short reserved1; + public short reserved2; + public short reserved3; + public short reserved4; + public short reserved5; + public short metricDataFormat; + public ushort numberOfHMetrics; + + public VerticalHeaderTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + Version = _fontData.ReadFixed(); + Ascender = _fontData.ReadFWord(); + Descender = _fontData.ReadFWord(); + LineGap = _fontData.ReadFWord(); + AdvanceWidthMax = _fontData.ReadUFWord(); + MinLeftSideBearing = _fontData.ReadFWord(); + MinRightSideBearing = _fontData.ReadFWord(); + xMaxExtent = _fontData.ReadFWord(); + caretSlopeRise = _fontData.ReadShort(); + caretSlopeRun = _fontData.ReadShort(); + reserved1 = _fontData.ReadShort(); + reserved2 = _fontData.ReadShort(); + reserved3 = _fontData.ReadShort(); + reserved4 = _fontData.ReadShort(); + reserved5 = _fontData.ReadShort(); + metricDataFormat = _fontData.ReadShort(); + numberOfHMetrics = _fontData.ReadUShort(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + internal class VerticalMetrics : OpenTypeFontTable + { + public const string Tag = "----"; + + // code comes from HorizontalMetrics + public ushort advanceWidth; + public short lsb; + + public VerticalMetrics(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + advanceWidth = _fontData.ReadUFWord(); + lsb = _fontData.ReadFWord(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// The vertical Metrics table allows you to specify the vertical spacing for each glyph in a + /// vertical font. This table consists of either one or two arrays that contain metric + /// information (the advance heights and top sidebearings) for the vertical layout of each + /// of the glyphs in the font. + /// + internal class VerticalMetricsTable : OpenTypeFontTable + { + // UNDONE + public const string Tag = TableTagNames.VMtx; + + // code comes from HorizontalMetricsTable + public HorizontalMetrics[] metrics; + public FWord[] leftSideBearing; + + public VerticalMetricsTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + throw new NotImplementedException("VerticalMetricsTable"); + } + + public void Read() + { + try + { + HorizontalHeaderTable hhea = _fontData.hhea; + MaximumProfileTable maxp = _fontData.maxp; + if (hhea != null && maxp != null) + { + int numMetrics = hhea.numberOfHMetrics; //->NumberOfHMetrics(); + int numLsbs = maxp.numGlyphs - numMetrics; + + Debug.Assert(numMetrics != 0); + Debug.Assert(numLsbs >= 0); + + metrics = new HorizontalMetrics[numMetrics]; + for (int idx = 0; idx < numMetrics; idx++) + metrics[idx] = new HorizontalMetrics(_fontData); + + if (numLsbs > 0) + { + leftSideBearing = new FWord[numLsbs]; + for (int idx = 0; idx < numLsbs; idx++) + leftSideBearing[idx] = _fontData.ReadFWord(); + } + } + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table establishes the memory requirements for this font. + /// Fonts with CFF data must use Version 0.5 of this table, specifying only the numGlyphs field. + /// Fonts with TrueType outlines must use Version 1.0 of this table, where all data is required. + /// Both formats of OpenType require a 'maxp' table because a number of applications call the + /// Windows GetFontData() API on the 'maxp' table to determine the number of glyphs in the font. + /// + internal class MaximumProfileTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.MaxP; + + public Fixed version; + public ushort numGlyphs; + public ushort maxPoints; + public ushort maxContours; + public ushort maxCompositePoints; + public ushort maxCompositeContours; + public ushort maxZones; + public ushort maxTwilightPoints; + public ushort maxStorage; + public ushort maxFunctionDefs; + public ushort maxInstructionDefs; + public ushort maxStackElements; + public ushort maxSizeOfInstructions; + public ushort maxComponentElements; + public ushort maxComponentDepth; + + public MaximumProfileTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + version = _fontData.ReadFixed(); + numGlyphs = _fontData.ReadUShort(); + maxPoints = _fontData.ReadUShort(); + maxContours = _fontData.ReadUShort(); + maxCompositePoints = _fontData.ReadUShort(); + maxCompositeContours = _fontData.ReadUShort(); + maxZones = _fontData.ReadUShort(); + maxTwilightPoints = _fontData.ReadUShort(); + maxStorage = _fontData.ReadUShort(); + maxFunctionDefs = _fontData.ReadUShort(); + maxInstructionDefs = _fontData.ReadUShort(); + maxStackElements = _fontData.ReadUShort(); + maxSizeOfInstructions = _fontData.ReadUShort(); + maxComponentElements = _fontData.ReadUShort(); + maxComponentDepth = _fontData.ReadUShort(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// The naming table allows multilingual strings to be associated with the OpenTypeTM font file. + /// These strings can represent copyright notices, font names, family names, style names, and so on. + /// To keep this table short, the font manufacturer may wish to make a limited set of entries in some + /// small set of languages; later, the font can be "localized" and the strings translated or added. + /// Other parts of the OpenType font file that require these strings can then refer to them simply by + /// their index number. Clients that need a particular string can look it up by its platform ID, character + /// encoding ID, language ID and name ID. Note that some platforms may require single byte character + /// strings, while others may require double byte strings. + /// + /// For historical reasons, some applications which install fonts perform Version control using Macintosh + /// platform (platform ID 1) strings from the 'name' table. Because of this, we strongly recommend that + /// the 'name' table of all fonts include Macintosh platform strings and that the syntax of the Version + /// number (name id 5) follows the guidelines given in this document. + /// + internal class NameTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Name; + + /// + /// Get the font family name. + /// + public string Name = String.Empty; + + /// + /// Get the font subfamily name. + /// + public string Style = String.Empty; + + /// + /// Get the full font name. + /// + public string FullFontName = String.Empty; + + public ushort format; + public ushort count; + public ushort stringOffset; + + byte[] bytes; + + public NameTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { +#if DEBUG + _fontData.Position = DirectoryEntry.Offset; +#endif + bytes = new byte[DirectoryEntry.PaddedLength]; + Buffer.BlockCopy(_fontData.FontSource.Bytes, DirectoryEntry.Offset, bytes, 0, DirectoryEntry.Length); + + format = _fontData.ReadUShort(); + count = _fontData.ReadUShort(); + stringOffset = _fontData.ReadUShort(); + + for (int idx = 0; idx < count; idx++) + { + NameRecord nrec = ReadNameRecord(); + byte[] value = new byte[nrec.length]; + Buffer.BlockCopy(_fontData.FontSource.Bytes, DirectoryEntry.Offset + stringOffset + nrec.offset, value, 0, nrec.length); + + //Debug.WriteLine(nrec.platformID.ToString()); + + // Read font name and style in US English. + if (nrec.platformID == 0 || nrec.platformID == 3) + { + // Font Family name. Up to four fonts can share the Font Family name, + // forming a font style linking group (regular, italic, bold, bold italic - + // as defined by OS/2.fsSelection bit settings). + if (nrec.nameID == 1 && nrec.languageID == 0x0409) + { + if (String.IsNullOrEmpty(Name)) + Name = Encoding.BigEndianUnicode.GetString(value, 0, value.Length); + } + + // Font Subfamily name. The Font Subfamily name distinguishes the font in a + // group with the same Font Family name (name ID 1). This is assumed to + // address style (italic, oblique) and weight (light, bold, black, etc.). + // A font with no particular differences in weight or style (e.g. medium weight, + // not italic and fsSelection bit 6 set) should have the string “Regular” stored in + // this position. + if (nrec.nameID == 2 && nrec.languageID == 0x0409) + { + if (String.IsNullOrEmpty(Style)) + Style = Encoding.BigEndianUnicode.GetString(value, 0, value.Length); + } + + // Full font name; a combination of strings 1 and 2, or a similar human-readable + // variant. If string 2 is "Regular", it is sometimes omitted from name ID 4. + if (nrec.nameID == 4 && nrec.languageID == 0x0409) + { + if (String.IsNullOrEmpty(FullFontName)) + FullFontName = Encoding.BigEndianUnicode.GetString(value, 0, value.Length); + } + } + } + Debug.Assert(!String.IsNullOrEmpty(Name)); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + + NameRecord ReadNameRecord() + { + NameRecord nrec = new NameRecord(); + nrec.platformID = _fontData.ReadUShort(); + nrec.encodingID = _fontData.ReadUShort(); + nrec.languageID = _fontData.ReadUShort(); + nrec.nameID = _fontData.ReadUShort(); + nrec.length = _fontData.ReadUShort(); + nrec.offset = _fontData.ReadUShort(); + return nrec; + } + + class NameRecord + { + public ushort platformID; + public ushort encodingID; + public ushort languageID; + public ushort nameID; + public ushort length; + public ushort offset; + } + } + + /// + /// The OS/2 table consists of a set of Metrics that are required in OpenType fonts. + /// + internal class OS2Table : OpenTypeFontTable + { + public const string Tag = TableTagNames.OS2; + + [Flags] + public enum FontSelectionFlags : ushort + { + Italic = 1 << 0, + Bold = 1 << 5, + Regular = 1 << 6, + } + + public ushort version; + public short xAvgCharWidth; + public ushort usWeightClass; + public ushort usWidthClass; + public ushort fsType; + public short ySubscriptXSize; + public short ySubscriptYSize; + public short ySubscriptXOffset; + public short ySubscriptYOffset; + public short ySuperscriptXSize; + public short ySuperscriptYSize; + public short ySuperscriptXOffset; + public short ySuperscriptYOffset; + public short yStrikeoutSize; + public short yStrikeoutPosition; + public short sFamilyClass; + public byte[] panose; // = new byte[10]; + public uint ulUnicodeRange1; // Bits 0-31 + public uint ulUnicodeRange2; // Bits 32-63 + public uint ulUnicodeRange3; // Bits 64-95 + public uint ulUnicodeRange4; // Bits 96-127 + public string achVendID; // = ""; + public ushort fsSelection; + public ushort usFirstCharIndex; + public ushort usLastCharIndex; + public short sTypoAscender; + public short sTypoDescender; + public short sTypoLineGap; + public ushort usWinAscent; + public ushort usWinDescent; + // Version >= 1 + public uint ulCodePageRange1; // Bits 0-31 + public uint ulCodePageRange2; // Bits 32-63 + // Version >= 2 + public short sxHeight; + public short sCapHeight; + public ushort usDefaultChar; + public ushort usBreakChar; + public ushort usMaxContext; + + public OS2Table(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + version = _fontData.ReadUShort(); + xAvgCharWidth = _fontData.ReadShort(); + usWeightClass = _fontData.ReadUShort(); + usWidthClass = _fontData.ReadUShort(); + fsType = _fontData.ReadUShort(); + ySubscriptXSize = _fontData.ReadShort(); + ySubscriptYSize = _fontData.ReadShort(); + ySubscriptXOffset = _fontData.ReadShort(); + ySubscriptYOffset = _fontData.ReadShort(); + ySuperscriptXSize = _fontData.ReadShort(); + ySuperscriptYSize = _fontData.ReadShort(); + ySuperscriptXOffset = _fontData.ReadShort(); + ySuperscriptYOffset = _fontData.ReadShort(); + yStrikeoutSize = _fontData.ReadShort(); + yStrikeoutPosition = _fontData.ReadShort(); + sFamilyClass = _fontData.ReadShort(); + panose = _fontData.ReadBytes(10); + ulUnicodeRange1 = _fontData.ReadULong(); + ulUnicodeRange2 = _fontData.ReadULong(); + ulUnicodeRange3 = _fontData.ReadULong(); + ulUnicodeRange4 = _fontData.ReadULong(); + achVendID = _fontData.ReadString(4); + fsSelection = _fontData.ReadUShort(); + usFirstCharIndex = _fontData.ReadUShort(); + usLastCharIndex = _fontData.ReadUShort(); + sTypoAscender = _fontData.ReadShort(); + sTypoDescender = _fontData.ReadShort(); + sTypoLineGap = _fontData.ReadShort(); + usWinAscent = _fontData.ReadUShort(); + usWinDescent = _fontData.ReadUShort(); + + if (version >= 1) + { + ulCodePageRange1 = _fontData.ReadULong(); + ulCodePageRange2 = _fontData.ReadULong(); + + if (version >= 2) + { + sxHeight = _fontData.ReadShort(); + sCapHeight = _fontData.ReadShort(); + usDefaultChar = _fontData.ReadUShort(); + usBreakChar = _fontData.ReadUShort(); + usMaxContext = _fontData.ReadUShort(); + } + } + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + + public bool IsBold + { + get { return (fsSelection & (ushort)FontSelectionFlags.Bold) != 0; } + } + + public bool IsItalic + { + get { return (fsSelection & (ushort)FontSelectionFlags.Italic) != 0; } + } + } + + /// + /// This table contains additional information needed to use TrueType or OpenTypeTM fonts + /// on PostScript printers. + /// + internal class PostScriptTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Post; + + public Fixed formatType; + public float italicAngle; + public FWord underlinePosition; + public FWord underlineThickness; + public ulong isFixedPitch; + public ulong minMemType42; + public ulong maxMemType42; + public ulong minMemType1; + public ulong maxMemType1; + + public PostScriptTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + Read(); + } + + public void Read() + { + try + { + formatType = _fontData.ReadFixed(); + italicAngle = _fontData.ReadFixed() / 65536f; + underlinePosition = _fontData.ReadFWord(); + underlineThickness = _fontData.ReadFWord(); + isFixedPitch = _fontData.ReadULong(); + minMemType42 = _fontData.ReadULong(); + maxMemType42 = _fontData.ReadULong(); + minMemType1 = _fontData.ReadULong(); + maxMemType1 = _fontData.ReadULong(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table contains a list of values that can be referenced by instructions. + /// They can be used, among other things, to control characteristics for different glyphs. + /// The length of the table must be an integral number of FWORD units. + /// + internal class ControlValueTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.Cvt; + + FWord[] array; // List of n values referenceable by instructions. n is the number of FWORD items that fit in the size of the table. + + public ControlValueTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry.Tag = TableTagNames.Cvt; + DirectoryEntry = fontData.TableDictionary[TableTagNames.Cvt]; + Read(); + } + + public void Read() + { + try + { + int length = DirectoryEntry.Length / 2; + array = new FWord[length]; + for (int idx = 0; idx < length; idx++) + array[idx] = _fontData.ReadFWord(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table is similar to the CVT Program, except that it is only run once, when the font is first used. + /// It is used only for FDEFs and IDEFs. Thus the CVT Program need not contain function definitions. + /// However, the CVT Program may redefine existing FDEFs or IDEFs. + /// + internal class FontProgram : OpenTypeFontTable + { + public const string Tag = TableTagNames.Fpgm; + + byte[] bytes; // Instructions. n is the number of BYTE items that fit in the size of the table. + + public FontProgram(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry.Tag = TableTagNames.Fpgm; + DirectoryEntry = fontData.TableDictionary[TableTagNames.Fpgm]; + Read(); + } + + public void Read() + { + try + { + int length = DirectoryEntry.Length; + bytes = new byte[length]; + for (int idx = 0; idx < length; idx++) + bytes[idx] = _fontData.ReadByte(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// The Control Value Program consists of a set of TrueType instructions that will be executed whenever the font or + /// point size or transformation matrix change and before each glyph is interpreted. Any instruction is legal in the + /// CVT Program but since no glyph is associated with it, instructions intended to move points within a particular + /// glyph outline cannot be used in the CVT Program. The name 'prep' is anachronistic. + /// + internal class ControlValueProgram : OpenTypeFontTable + { + public const string Tag = TableTagNames.Prep; + + byte[] bytes; // Set of instructions executed whenever point size or font or transformation change. n is the number of BYTE items that fit in the size of the table. + + public ControlValueProgram(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry.Tag = TableTagNames.Prep; + DirectoryEntry = fontData.TableDictionary[TableTagNames.Prep]; + Read(); + } + + public void Read() + { + try + { + int length = DirectoryEntry.Length; + bytes = new byte[length]; + for (int idx = 0; idx < length; idx++) + bytes[idx] = _fontData.ReadByte(); + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } + + /// + /// This table contains information that describes the glyphs in the font in the TrueType outline format. + /// Information regarding the rasterizer (scaler) refers to the TrueType rasterizer. + /// + internal class GlyphSubstitutionTable : OpenTypeFontTable + { + public const string Tag = TableTagNames.GSUB; + + public GlyphSubstitutionTable(OpenTypeFontface fontData) + : base(fontData, Tag) + { + DirectoryEntry.Tag = TableTagNames.GSUB; + DirectoryEntry = fontData.TableDictionary[TableTagNames.GSUB]; + Read(); + } + + public void Read() + { + try + { + } + catch (Exception ex) + { + throw new InvalidOperationException(PSSR.ErrorReadingFontData, ex); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontWriter.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontWriter.cs new file mode 100644 index 00000000..5b9bc5a2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontWriter.cs @@ -0,0 +1,59 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.IO; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Represents a writer for True Type font files. + /// + internal class OpenTypeFontWriter : FontWriter + { + /// + /// Initializes a new instance of the class. + /// + public OpenTypeFontWriter(Stream stream) + : base(stream) + { } + + /// + /// Writes a table name. + /// + public void WriteTag(string tag) + { + Debug.Assert(tag.Length == 4); + WriteByte((byte)(tag[0])); + WriteByte((byte)(tag[1])); + WriteByte((byte)(tag[2])); + WriteByte((byte)(tag[3])); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontface.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontface.cs new file mode 100644 index 00000000..d4eba796 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontface.cs @@ -0,0 +1,745 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define VERBOSE_ + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; +using System.IO; +#if GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif +using PdfSharp.Fonts; +#if !EDF_CORE +using PdfSharp.Drawing; +using PdfSharp.Internal; +#endif + +using Fixed = System.Int32; +using FWord = System.Int16; +using UFWord = System.UInt16; + +#pragma warning disable 0649 + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Represents an OpenType fontface in memory. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal sealed class OpenTypeFontface + { + // Implementation Notes + // OpenTypeFontface represents a 'decompiled' font file in memory. + // + // * An OpenTypeFontface can belong to more than one + // XGlyphTypeface because of StyleSimulations. + // + // * Currently there is a one to one relationship to XFontSource. + // + // * Consider OpenTypeFontface as an decompiled XFontSource. + // + // http://www.microsoft.com/typography/otspec/ + + /// + /// Shallow copy for font subset. + /// + OpenTypeFontface(OpenTypeFontface fontface) + { + _offsetTable = fontface._offsetTable; + _fullFaceName = fontface._fullFaceName; + } + + /// + /// Initializes a new instance of the class. + /// + public OpenTypeFontface(byte[] data, string faceName) + { + _fullFaceName = faceName; + // Always save a copy of the font bytes. + int length = data.Length; + //FontSource = new XFontSource(faceName, new byte[length]); + Array.Copy(data, FontSource.Bytes, length); + Read(); + } + + public OpenTypeFontface(XFontSource fontSource) + { + FontSource = fontSource; + Read(); + _fullFaceName = name.FullFontName; + } + + public static OpenTypeFontface CetOrCreateFrom(XFontSource fontSource) + { + OpenTypeFontface fontface; + if (OpenTypeFontfaceCache.TryGetFontface(fontSource.Key, out fontface)) + { + return fontface; + } + // Each font source already contains its OpenTypeFontface. + Debug.Assert(fontSource.Fontface != null); + fontface = OpenTypeFontfaceCache.AddFontface(fontSource.Fontface); + Debug.Assert(ReferenceEquals(fontSource.Fontface, fontface)); + return fontface; + } + + /// + /// Gets the full face name from the name table. + /// Name is also used as the key. + /// + public string FullFaceName + { + get { return _fullFaceName; } + } + readonly string _fullFaceName; + + public ulong CheckSum + { + get + { + if (_checkSum == 0) + _checkSum = FontHelper.CalcChecksum(FontSource.Bytes); + return _checkSum; + } + } + ulong _checkSum; + + /// + /// Gets the bytes that represents the font data. + /// + public XFontSource FontSource + { + get { return _fontSource; } + private set + { + // Stop working if font was not found. + if (value == null) + throw new InvalidOperationException("Font cannot be resolved."); + _fontSource = value; + } + } + XFontSource _fontSource; + + internal FontTechnology _fontTechnology; + + internal OffsetTable _offsetTable; + + /// + /// The dictionary of all font tables. + /// + internal Dictionary TableDictionary = new Dictionary(); + + // Keep names identical to OpenType spec. + // ReSharper disable InconsistentNaming + internal CMapTable cmap; + internal ControlValueTable cvt; + internal FontProgram fpgm; + internal MaximumProfileTable maxp; + internal NameTable name; + internal ControlValueProgram prep; + internal FontHeaderTable head; + internal HorizontalHeaderTable hhea; + internal HorizontalMetricsTable hmtx; + internal OS2Table os2; + internal PostScriptTable post; + internal GlyphDataTable glyf; + internal IndexToLocationTable loca; + internal GlyphSubstitutionTable gsub; + internal VerticalHeaderTable vhea; // TODO + internal VerticalMetricsTable vmtx; // TODO + // ReSharper restore InconsistentNaming + + public bool CanRead + { + get { return FontSource != null; } + } + + public bool CanWrite + { + get { return FontSource == null; } + } + + /// + /// Adds the specified table to this font image. + /// + public void AddTable(OpenTypeFontTable fontTable) + { + if (!CanWrite) + throw new InvalidOperationException("Font image cannot be modified."); + + if (fontTable == null) + throw new ArgumentNullException("fontTable"); + + if (fontTable._fontData == null) + { + fontTable._fontData = this; + } + else + { + Debug.Assert(fontTable._fontData.CanRead); + // Create a reference to this font table + fontTable = new IRefFontTable(this, fontTable); + } + + //Debug.Assert(fontTable.FontData == null); + //fontTable.fontData = this; + + TableDictionary[fontTable.DirectoryEntry.Tag] = fontTable.DirectoryEntry; + switch (fontTable.DirectoryEntry.Tag) + { + case TableTagNames.CMap: + cmap = fontTable as CMapTable; + break; + + case TableTagNames.Cvt: + cvt = fontTable as ControlValueTable; + break; + + case TableTagNames.Fpgm: + fpgm = fontTable as FontProgram; + break; + + case TableTagNames.MaxP: + maxp = fontTable as MaximumProfileTable; + break; + + case TableTagNames.Name: + name = fontTable as NameTable; + break; + + case TableTagNames.Head: + head = fontTable as FontHeaderTable; + break; + + case TableTagNames.HHea: + hhea = fontTable as HorizontalHeaderTable; + break; + + case TableTagNames.HMtx: + hmtx = fontTable as HorizontalMetricsTable; + break; + + case TableTagNames.OS2: + os2 = fontTable as OS2Table; + break; + + case TableTagNames.Post: + post = fontTable as PostScriptTable; + break; + + case TableTagNames.Glyf: + glyf = fontTable as GlyphDataTable; + break; + + case TableTagNames.Loca: + loca = fontTable as IndexToLocationTable; + break; + + case TableTagNames.GSUB: + gsub = fontTable as GlyphSubstitutionTable; + break; + + case TableTagNames.Prep: + prep = fontTable as ControlValueProgram; + break; + } + } + + /// + /// Reads all required tables from the font data. + /// + internal void Read() + { + // Determine font technology + // ReSharper disable InconsistentNaming + const uint OTTO = 0x4f54544f; // Adobe OpenType CFF data, tag: 'OTTO' + const uint TTCF = 0x74746366; // TrueType Collection tag: 'ttcf' + // ReSharper restore InconsistentNaming + try + { +#if DEBUG_ + if (Name == "Cambria") + Debug-Break.Break(); +#endif + + // Check if data is a TrueType collection font. + uint startTag = ReadULong(); + if (startTag == TTCF) + { + _fontTechnology = FontTechnology.TrueTypeCollection; + throw new InvalidOperationException("TrueType collection fonts are not yet supported by PDFsharp."); + } + + // Read offset table + _offsetTable.Version = startTag; + _offsetTable.TableCount = ReadUShort(); + _offsetTable.SearchRange = ReadUShort(); + _offsetTable.EntrySelector = ReadUShort(); + _offsetTable.RangeShift = ReadUShort(); + + // Move to table dictionary at position 12 + Debug.Assert(_pos == 12); + //tableDictionary = (offsetTable.TableCount); + + if (_offsetTable.Version == OTTO) + _fontTechnology = FontTechnology.PostscriptOutlines; + else + _fontTechnology = FontTechnology.TrueTypeOutlines; + + for (int idx = 0; idx < _offsetTable.TableCount; idx++) + { + TableDirectoryEntry entry = TableDirectoryEntry.ReadFrom(this); + TableDictionary.Add(entry.Tag, entry); +#if VERBOSE + Debug.WriteLine(String.Format("Font table: {0}", entry.Tag)); +#endif + } + + // PDFlib checks this, but it is not part of the OpenType spec anymore + if (TableDictionary.ContainsKey("bhed")) + throw new NotSupportedException("Bitmap fonts are not supported by PDFsharp."); + + // Read required tables + if (Seek(CMapTable.Tag) != -1) + cmap = new CMapTable(this); + + if (Seek(ControlValueTable.Tag) != -1) + cvt = new ControlValueTable(this); + + if (Seek(FontProgram.Tag) != -1) + fpgm = new FontProgram(this); + + if (Seek(MaximumProfileTable.Tag) != -1) + maxp = new MaximumProfileTable(this); + + if (Seek(NameTable.Tag) != -1) + name = new NameTable(this); + + if (Seek(FontHeaderTable.Tag) != -1) + head = new FontHeaderTable(this); + + if (Seek(HorizontalHeaderTable.Tag) != -1) + hhea = new HorizontalHeaderTable(this); + + if (Seek(HorizontalMetricsTable.Tag) != -1) + hmtx = new HorizontalMetricsTable(this); + + if (Seek(OS2Table.Tag) != -1) + os2 = new OS2Table(this); + + if (Seek(PostScriptTable.Tag) != -1) + post = new PostScriptTable(this); + + if (Seek(GlyphDataTable.Tag) != -1) + glyf = new GlyphDataTable(this); + + if (Seek(IndexToLocationTable.Tag) != -1) + loca = new IndexToLocationTable(this); + + if (Seek(GlyphSubstitutionTable.Tag) != -1) + gsub = new GlyphSubstitutionTable(this); + + if (Seek(ControlValueProgram.Tag) != -1) + prep = new ControlValueProgram(this); + } + catch (Exception) + { + GetType(); + throw; + } + } + + /// + /// Creates a new font image that is a subset of this font image containing only the specified glyphs. + /// + public OpenTypeFontface CreateFontSubSet(Dictionary glyphs, bool cidFont) + { + // Create new font image + OpenTypeFontface fontData = new OpenTypeFontface(this); + + // Create new loca and glyf table + IndexToLocationTable locaNew = new IndexToLocationTable(); + locaNew.ShortIndex = loca.ShortIndex; + GlyphDataTable glyfNew = new GlyphDataTable(); + + // Add all required tables + //fontData.AddTable(os2); + if (!cidFont) + fontData.AddTable(cmap); + if (cvt != null) + fontData.AddTable(cvt); + if (fpgm != null) + fontData.AddTable(fpgm); + fontData.AddTable(glyfNew); + fontData.AddTable(head); + fontData.AddTable(hhea); + fontData.AddTable(hmtx); + fontData.AddTable(locaNew); + if (maxp != null) + fontData.AddTable(maxp); + //fontData.AddTable(name); + if (prep != null) + fontData.AddTable(prep); + + // Get closure of used glyphs. + glyf.CompleteGlyphClosure(glyphs); + + // Create a sorted array of all used glyphs. + int glyphCount = glyphs.Count; + int[] glyphArray = new int[glyphCount]; + glyphs.Keys.CopyTo(glyphArray, 0); + Array.Sort(glyphArray); + + // Calculate new size of glyph table. + int size = 0; + for (int idx = 0; idx < glyphCount; idx++) + size += glyf.GetGlyphSize(glyphArray[idx]); + glyfNew.DirectoryEntry.Length = size; + + // Create new loca table + int numGlyphs = maxp.numGlyphs; + locaNew.LocaTable = new int[numGlyphs + 1]; + + // Create new glyf table + glyfNew.GlyphTable = new byte[glyfNew.DirectoryEntry.PaddedLength]; + + // Fill new glyf and loca table + int glyphOffset = 0; + int glyphIndex = 0; + for (int idx = 0; idx < numGlyphs; idx++) + { + locaNew.LocaTable[idx] = glyphOffset; + if (glyphIndex < glyphCount && glyphArray[glyphIndex] == idx) + { + glyphIndex++; + byte[] bytes = glyf.GetGlyphData(idx); + int length = bytes.Length; + if (length > 0) + { + Buffer.BlockCopy(bytes, 0, glyfNew.GlyphTable, glyphOffset, length); + glyphOffset += length; + } + } + } + locaNew.LocaTable[numGlyphs] = glyphOffset; + + // Compile font tables into byte array + fontData.Compile(); + + return fontData; + } + + /// + /// Compiles the font to its binary representation. + /// + void Compile() + { + MemoryStream stream = new MemoryStream(); + OpenTypeFontWriter writer = new OpenTypeFontWriter(stream); + + int tableCount = TableDictionary.Count; + int selector = _entrySelectors[tableCount]; + + _offsetTable.Version = 0x00010000; + _offsetTable.TableCount = tableCount; + _offsetTable.SearchRange = (ushort)((1 << selector) * 16); + _offsetTable.EntrySelector = (ushort)selector; + _offsetTable.RangeShift = (ushort)((tableCount - (1 << selector)) * 16); + _offsetTable.Write(writer); + + // Sort tables by tag name + string[] tags = new string[tableCount]; + TableDictionary.Keys.CopyTo(tags, 0); + Array.Sort(tags, StringComparer.Ordinal); + +#if VERBOSE + Debug.WriteLine("Start Compile"); +#endif + // Write tables in alphabetical order + int tablePosition = 12 + 16 * tableCount; + for (int idx = 0; idx < tableCount; idx++) + { + TableDirectoryEntry entry = TableDictionary[tags[idx]]; +#if DEBUG + if (entry.Tag == "glyf" || entry.Tag == "loca") + GetType(); +#endif + entry.FontTable.PrepareForCompilation(); + entry.Offset = tablePosition; + writer.Position = tablePosition; + entry.FontTable.Write(writer); + int endPosition = writer.Position; + tablePosition = endPosition; + writer.Position = 12 + 16 * idx; + entry.Write(writer); +#if VERBOSE + Debug.WriteLine(String.Format(" Write Table '{0}', offset={1}, length={2}, checksum={3}, ", entry.Tag, entry.Offset, entry.Length, entry.CheckSum)); +#endif + } +#if VERBOSE + Debug.WriteLine("End Compile"); +#endif + writer.Stream.Flush(); + int l = (int)writer.Stream.Length; + FontSource = XFontSource.CreateCompiledFont(stream.ToArray()); + } + // 2^entrySelector[n] <= n + static readonly int[] _entrySelectors = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; + + public int Position + { + get { return _pos; } + set { _pos = value; } + } + int _pos; + + public int Seek(string tag) + { + if (TableDictionary.ContainsKey(tag)) + { + _pos = TableDictionary[tag].Offset; + return _pos; + } + return -1; + } + + public int SeekOffset(int offset) + { + _pos += offset; + return _pos; + } + + /// + /// Reads a System.Byte. + /// + public byte ReadByte() + { + return _fontSource.Bytes[_pos++]; + } + + /// + /// Reads a System.Int16. + /// + public short ReadShort() + { + int pos = _pos; + _pos += 2; + return (short)((_fontSource.Bytes[pos] << 8) | (_fontSource.Bytes[pos + 1])); + } + + /// + /// Reads a System.UInt16. + /// + public ushort ReadUShort() + { + int pos = _pos; + _pos += 2; + return (ushort)((_fontSource.Bytes[pos] << 8) | (_fontSource.Bytes[pos + 1])); + } + + /// + /// Reads a System.Int32. + /// + public int ReadLong() + { + int pos = _pos; + _pos += 4; + return (_fontSource.Bytes[pos] << 24) | (_fontSource.Bytes[pos + 1] << 16) | (_fontSource.Bytes[pos + 2] << 8) | (_fontSource.Bytes[pos + 3]); + } + + /// + /// Reads a System.UInt32. + /// + public uint ReadULong() + { + int pos = _pos; + _pos += 4; + return (uint)((_fontSource.Bytes[pos] << 24) | (_fontSource.Bytes[pos + 1] << 16) | (_fontSource.Bytes[pos + 2] << 8) | (_fontSource.Bytes[pos + 3])); + } + + /// + /// Reads a System.Int32. + /// + public Fixed ReadFixed() + { + int pos = _pos; + _pos += 4; + return (_fontSource.Bytes[pos] << 24) | (_fontSource.Bytes[pos + 1] << 16) | (_fontSource.Bytes[pos + 2] << 8) | (_fontSource.Bytes[pos + 3]); + } + + /// + /// Reads a System.Int16. + /// + public short ReadFWord() + { + int pos = _pos; + _pos += 2; + return (short)((_fontSource.Bytes[pos] << 8) | (_fontSource.Bytes[pos + 1])); + } + + /// + /// Reads a System.UInt16. + /// + public ushort ReadUFWord() + { + int pos = _pos; + _pos += 2; + return (ushort)((_fontSource.Bytes[pos] << 8) | (_fontSource.Bytes[pos + 1])); + } + + /// + /// Reads a System.Int64. + /// + public long ReadLongDate() + { + int pos = _pos; + _pos += 8; + byte[] bytes = _fontSource.Bytes; + return (((long)bytes[pos]) << 56) | (((long)bytes[pos + 1]) << 48) | (((long)bytes[pos + 2]) << 40) | (((long)bytes[pos + 3]) << 32) | + (((long)bytes[pos + 4]) << 24) | (((long)bytes[pos + 5]) << 16) | (((long)bytes[pos + 6]) << 8) | bytes[pos + 7]; + } + + /// + /// Reads a System.String with the specified size. + /// + public string ReadString(int size) + { + char[] chars = new char[size]; + for (int idx = 0; idx < size; idx++) + chars[idx] = (char)_fontSource.Bytes[_pos++]; + return new string(chars); + } + + /// + /// Reads a System.Byte[] with the specified size. + /// + public byte[] ReadBytes(int size) + { + byte[] bytes = new byte[size]; + for (int idx = 0; idx < size; idx++) + bytes[idx] = _fontSource.Bytes[_pos++]; + return bytes; + } + + /// + /// Reads the specified buffer. + /// + public void Read(byte[] buffer) + { + Read(buffer, 0, buffer.Length); + } + + /// + /// Reads the specified buffer. + /// + public void Read(byte[] buffer, int offset, int length) + { + Buffer.BlockCopy(_fontSource.Bytes, _pos, buffer, offset, length); + _pos += length; + } + + /// + /// Reads a System.Char[4] as System.String. + /// + public string ReadTag() + { + return ReadString(4); + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + internal string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get { return string.Format(CultureInfo.InvariantCulture, "OpenType fontfaces: {0}", _fullFaceName); } + } + + /// + /// Represents the font offset table. + /// + internal struct OffsetTable + { + /// + /// 0x00010000 for Version 1.0. + /// + public uint Version; + + /// + /// Number of tables. + /// + public int TableCount; + + /// + /// (Maximum power of 2 ≤ numTables) x 16. + /// + public ushort SearchRange; + + /// + /// Log2(maximum power of 2 ≤ numTables). + /// + public ushort EntrySelector; + + /// + /// NumTables x 16-searchRange. + /// + public ushort RangeShift; + + /// + /// Writes the offset table. + /// + public void Write(OpenTypeFontWriter writer) + { + writer.WriteUInt(Version); + writer.WriteShort(TableCount); + writer.WriteUShort(SearchRange); + writer.WriteUShort(EntrySelector); + writer.WriteUShort(RangeShift); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs new file mode 100644 index 00000000..356b1f07 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/OpenTypeFontfaceCache.cs @@ -0,0 +1,157 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using PdfSharp.Internal; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Global table of all OpenType fontfaces cached by their face name and check sum. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + internal class OpenTypeFontfaceCache + { + OpenTypeFontfaceCache() + { + _fontfaceCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + _fontfacesByCheckSum = new Dictionary(); + } + + /// + /// Tries to get fontface by its key. + /// + public static bool TryGetFontface(string key, out OpenTypeFontface fontface) + { + try + { + Lock.EnterFontFactory(); + bool result = Singleton._fontfaceCache.TryGetValue(key, out fontface); + return result; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Tries to get fontface by its check sum. + /// + public static bool TryGetFontface(ulong checkSum, out OpenTypeFontface fontface) + { + try + { + Lock.EnterFontFactory(); + bool result = Singleton._fontfacesByCheckSum.TryGetValue(checkSum, out fontface); + return result; + } + finally { Lock.ExitFontFactory(); } + } + + public static OpenTypeFontface AddFontface(OpenTypeFontface fontface) + { + try + { + Lock.EnterFontFactory(); + OpenTypeFontface fontfaceCheck; + if (TryGetFontface(fontface.FullFaceName, out fontfaceCheck)) + { + if (fontfaceCheck.CheckSum != fontface.CheckSum) + throw new InvalidOperationException("OpenTypeFontface with same signature but different bytes."); + return fontfaceCheck; + } + Singleton._fontfaceCache.Add(fontface.FullFaceName, fontface); + Singleton._fontfacesByCheckSum.Add(fontface.CheckSum, fontface); + return fontface; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Gets the singleton. + /// + static OpenTypeFontfaceCache Singleton + { + get + { + // ReSharper disable once InvertIf + if (_singleton == null) + { + try + { + Lock.EnterFontFactory(); + if (_singleton == null) + _singleton = new OpenTypeFontfaceCache(); + } + finally { Lock.ExitFontFactory(); } + } + return _singleton; + } + } + static volatile OpenTypeFontfaceCache _singleton; + + internal static string GetCacheState() + { + StringBuilder state = new StringBuilder(); + state.Append("====================\n"); + state.Append("OpenType fontfaces by name\n"); + Dictionary.KeyCollection familyKeys = Singleton._fontfaceCache.Keys; + int count = familyKeys.Count; + string[] keys = new string[count]; + familyKeys.CopyTo(keys, 0); + Array.Sort(keys, StringComparer.OrdinalIgnoreCase); + foreach (string key in keys) + state.AppendFormat(" {0}: {1}\n", key, Singleton._fontfaceCache[key].DebuggerDisplay); + state.Append("\n"); + return state.ToString(); + } + + /// + /// Maps face name to OpenType fontface. + /// + readonly Dictionary _fontfaceCache; + + /// + /// Maps font source key to OpenType fontface. + /// + readonly Dictionary _fontfacesByCheckSum; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get { return string.Format(CultureInfo.InvariantCulture, "Fontfaces: {0}", _fontfaceCache.Count); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/TableDirectoryEntry.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/TableDirectoryEntry.cs new file mode 100644 index 00000000..6bdbe565 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/TableDirectoryEntry.cs @@ -0,0 +1,129 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#define VERBOSE_ + +using System.Diagnostics; + +//using Fixed = System.Int32; +//using FWord = System.Int16; +//using UFWord = System.UInt16; + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Represents an entry in the fonts table dictionary. + /// + internal class TableDirectoryEntry + { + /// + /// Initializes a new instance of the class. + /// + public TableDirectoryEntry() + { } + + /// + /// Initializes a new instance of the class. + /// + public TableDirectoryEntry(string tag) + { + Debug.Assert(tag.Length == 4); + Tag = tag; + //CheckSum = 0; + //Offset = 0; + //Length = 0; + //FontTable = null; + } + + /// + /// 4 -byte identifier. + /// + public string Tag; + + /// + /// CheckSum for this table. + /// + public uint CheckSum; + + /// + /// Offset from beginning of TrueType font file. + /// + public int Offset; + + /// + /// Actual length of this table in bytes. + /// + public int Length; + + /// + /// Gets the length rounded up to a multiple of four bytes. + /// + public int PaddedLength + { + get { return (Length + 3) & ~3; } + } + + /// + /// Associated font table. + /// + public OpenTypeFontTable FontTable; + + /// + /// Creates and reads a TableDirectoryEntry from the font image. + /// + public static TableDirectoryEntry ReadFrom(OpenTypeFontface fontData) + { + TableDirectoryEntry entry = new TableDirectoryEntry(); + entry.Tag = fontData.ReadTag(); + entry.CheckSum = fontData.ReadULong(); + entry.Offset = fontData.ReadLong(); + entry.Length = (int)fontData.ReadULong(); + return entry; + } + + public void Read(OpenTypeFontface fontData) + { + Tag = fontData.ReadTag(); + CheckSum = fontData.ReadULong(); + Offset = fontData.ReadLong(); + Length = (int)fontData.ReadULong(); + } + + public void Write(OpenTypeFontWriter writer) + { + Debug.Assert(Tag.Length == 4); + Debug.Assert(Offset != 0); + Debug.Assert(Length != 0); + writer.WriteTag(Tag); + writer.WriteUInt(CheckSum); + writer.WriteInt(Offset); + writer.WriteUInt((uint)Length); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/FontTechnology.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/FontTechnology.cs new file mode 100644 index 00000000..7dbee051 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/FontTechnology.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// Identifies the technology of an OpenType font file. + /// + enum FontTechnology + { + /// + /// Font is Adobe Postscript font in CFF. + /// + PostscriptOutlines, + + /// + /// Font is a TrueType font. + /// + TrueTypeOutlines, + + /// + /// Font is a TrueType font collection. + /// + TrueTypeCollection + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/TableTagNames.cs b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/TableTagNames.cs new file mode 100644 index 00000000..0bfb1fd1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts.OpenType/enums/TableTagNames.cs @@ -0,0 +1,211 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Fonts.OpenType +{ + /// + /// TrueType font table names. + /// + static class TableTagNames + { + // --- Required Tables --- + + /// + /// Character to glyph mapping. + /// + public const string CMap = "cmap"; + + /// + /// Font header . + /// + public const string Head = "head"; + + /// + /// Horizontal header. + /// + public const string HHea = "hhea"; + + /// + /// Horizontal Metrics. + /// + public const string HMtx = "hmtx"; + + /// + /// Maximum profile. + /// + public const string MaxP = "maxp"; + + /// + /// Naming table. + /// + public const string Name = "name"; + + /// + /// OS/2 and Windows specific Metrics. + /// + public const string OS2 = "OS/2"; + + /// + /// PostScript information. + /// + public const string Post = "post"; + + // --- Tables Related to TrueType Outlines --- + + /// + /// Control Value Table. + /// + public const string Cvt = "cvt "; + + /// + /// Font program. + /// + public const string Fpgm = "fpgm"; + + /// + /// Glyph data. + /// + public const string Glyf = "glyf"; + + /// + /// Index to location. + /// + public const string Loca = "loca"; + + /// + /// CVT Program. + /// + public const string Prep = "prep"; + + // --- Tables Related to PostScript Outlines --- + + /// + /// PostScript font program (compact font format). + /// + public const string Cff = "CFF"; + + /// + /// Vertical Origin. + /// + public const string VOrg = "VORG"; + + // --- Tables Related to Bitmap Glyphs --- + + /// + /// Embedded bitmap data. + /// + public const string EBDT = "EBDT"; + + /// + /// Embedded bitmap location data. + /// + public const string EBLC = "EBLC"; + + /// + /// Embedded bitmap scaling data. + /// + public const string EBSC = "EBSC"; + + // --- Advanced Typographic Tables --- + + /// + /// Baseline data. + /// + public const string BASE = "BASE"; + + /// + /// Glyph definition data. + /// + public const string GDEF = "GDEF"; + + /// + /// Glyph positioning data. + /// + public const string GPOS = "GPOS"; + + /// + /// Glyph substitution data. + /// + public const string GSUB = "GSUB"; + + /// + /// Justification data. + /// + public const string JSTF = "JSTF"; + + // --- Other OpenType Tables --- + + /// + /// Digital signature. + /// + public const string DSIG = "DSIG"; + + /// + /// Grid-fitting/Scan-conversion. + /// + public const string Gasp = "gasp"; + + /// + /// Horizontal device Metrics. + /// + public const string Hdmx = "hdmx"; + + /// + /// Kerning. + /// + public const string Kern = "kern"; + + /// + /// Linear threshold data. + /// + public const string LTSH = "LTSH"; + + /// + /// PCL 5 data. + /// + public const string PCLT = "PCLT"; + + /// + /// Vertical device Metrics. + /// + public const string VDMX = "VDMX"; + + /// + /// Vertical Header. + /// + public const string VHea = "vhea"; + + /// + /// Vertical Metrics. + /// + public const string VMtx = "vmtx"; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphList20.cs b/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphList20.cs new file mode 100644 index 00000000..5d892362 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphList20.cs @@ -0,0 +1,4325 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if DEBUG_ +namespace PdfSharp.Fonts +{ + /// + /// Pre-defined names for Unicode characters. + /// + /*public*/ class AdobeGlyphList20 + { + AdobeGlyphList20() { } + + public char A = '\u0041'; + public char AE = '\u00C6'; + public char AEacute = '\u01FC'; + public char AEmacron = '\u01E2'; + public char AEsmall = '\uF7E6'; + public char Aacute = '\u00C1'; + public char Aacutesmall = '\uF7E1'; + public char Abreve = '\u0102'; + public char Abreveacute = '\u1EAE'; + public char Abrevecyrillic = '\u04D0'; + public char Abrevedotbelow = '\u1EB6'; + public char Abrevegrave = '\u1EB0'; + public char Abrevehookabove = '\u1EB2'; + public char Abrevetilde = '\u1EB4'; + public char Acaron = '\u01CD'; + public char Acircle = '\u24B6'; + public char Acircumflex = '\u00C2'; + public char Acircumflexacute = '\u1EA4'; + public char Acircumflexdotbelow = '\u1EAC'; + public char Acircumflexgrave = '\u1EA6'; + public char Acircumflexhookabove = '\u1EA8'; + public char Acircumflexsmall = '\uF7E2'; + public char Acircumflextilde = '\u1EAA'; + public char Acute = '\uF6C9'; + public char Acutesmall = '\uF7B4'; + public char Acyrillic = '\u0410'; + public char Adblgrave = '\u0200'; + public char Adieresis = '\u00C4'; + public char Adieresiscyrillic = '\u04D2'; + public char Adieresismacron = '\u01DE'; + public char Adieresissmall = '\uF7E4'; + public char Adotbelow = '\u1EA0'; + public char Adotmacron = '\u01E0'; + public char Agrave = '\u00C0'; + public char Agravesmall = '\uF7E0'; + public char Ahookabove = '\u1EA2'; + public char Aiecyrillic = '\u04D4'; + public char Ainvertedbreve = '\u0202'; + public char Alpha = '\u0391'; + public char Alphatonos = '\u0386'; + public char Amacron = '\u0100'; + public char Amonospace = '\uFF21'; + public char Aogonek = '\u0104'; + public char Aring = '\u00C5'; + public char Aringacute = '\u01FA'; + public char Aringbelow = '\u1E00'; + public char Aringsmall = '\uF7E5'; + public char Asmall = '\uF761'; + public char Atilde = '\u00C3'; + public char Atildesmall = '\uF7E3'; + public char Aybarmenian = '\u0531'; + public char B = '\u0042'; + public char Bcircle = '\u24B7'; + public char Bdotaccent = '\u1E02'; + public char Bdotbelow = '\u1E04'; + public char Becyrillic = '\u0411'; + public char Benarmenian = '\u0532'; + public char Beta = '\u0392'; + public char Bhook = '\u0181'; + public char Blinebelow = '\u1E06'; + public char Bmonospace = '\uFF22'; + public char Brevesmall = '\uF6F4'; + public char Bsmall = '\uF762'; + public char Btopbar = '\u0182'; + public char C = '\u0043'; + public char Caarmenian = '\u053E'; + public char Cacute = '\u0106'; + public char Caron = '\uF6CA'; + public char Caronsmall = '\uF6F5'; + public char Ccaron = '\u010C'; + public char Ccedilla = '\u00C7'; + public char Ccedillaacute = '\u1E08'; + public char Ccedillasmall = '\uF7E7'; + public char Ccircle = '\u24B8'; + public char Ccircumflex = '\u0108'; + public char Cdot = '\u010A'; + public char Cdotaccent = '\u010A'; + public char Cedillasmall = '\uF7B8'; + public char Chaarmenian = '\u0549'; + public char Cheabkhasiancyrillic = '\u04BC'; + public char Checyrillic = '\u0427'; + public char Chedescenderabkhasiancyrillic = '\u04BE'; + public char Chedescendercyrillic = '\u04B6'; + public char Chedieresiscyrillic = '\u04F4'; + public char Cheharmenian = '\u0543'; + public char Chekhakassiancyrillic = '\u04CB'; + public char Cheverticalstrokecyrillic = '\u04B8'; + public char Chi = '\u03A7'; + public char Chook = '\u0187'; + public char Circumflexsmall = '\uF6F6'; + public char Cmonospace = '\uFF23'; + public char Coarmenian = '\u0551'; + public char Csmall = '\uF763'; + public char D = '\u0044'; + public char DZ = '\u01F1'; + public char DZcaron = '\u01C4'; + public char Daarmenian = '\u0534'; + public char Dafrican = '\u0189'; + public char Dcaron = '\u010E'; + public char Dcedilla = '\u1E10'; + public char Dcircle = '\u24B9'; + public char Dcircumflexbelow = '\u1E12'; + public char Dcroat = '\u0110'; + public char Ddotaccent = '\u1E0A'; + public char Ddotbelow = '\u1E0C'; + public char Decyrillic = '\u0414'; + public char Deicoptic = '\u03EE'; + public char Delta = '\u2206'; + public char Deltagreek = '\u0394'; + public char Dhook = '\u018A'; + public char Dieresis = '\uF6CB'; + public char DieresisAcute = '\uF6CC'; + public char DieresisGrave = '\uF6CD'; + public char Dieresissmall = '\uF7A8'; + public char Digammagreek = '\u03DC'; + public char Djecyrillic = '\u0402'; + public char Dlinebelow = '\u1E0E'; + public char Dmonospace = '\uFF24'; + public char Dotaccentsmall = '\uF6F7'; + public char Dslash = '\u0110'; + public char Dsmall = '\uF764'; + public char Dtopbar = '\u018B'; + public char Dz = '\u01F2'; + public char Dzcaron = '\u01C5'; + public char Dzeabkhasiancyrillic = '\u04E0'; + public char Dzecyrillic = '\u0405'; + public char Dzhecyrillic = '\u040F'; + public char E = '\u0045'; + public char Eacute = '\u00C9'; + public char Eacutesmall = '\uF7E9'; + public char Ebreve = '\u0114'; + public char Ecaron = '\u011A'; + public char Ecedillabreve = '\u1E1C'; + public char Echarmenian = '\u0535'; + public char Ecircle = '\u24BA'; + public char Ecircumflex = '\u00CA'; + public char Ecircumflexacute = '\u1EBE'; + public char Ecircumflexbelow = '\u1E18'; + public char Ecircumflexdotbelow = '\u1EC6'; + public char Ecircumflexgrave = '\u1EC0'; + public char Ecircumflexhookabove = '\u1EC2'; + public char Ecircumflexsmall = '\uF7EA'; + public char Ecircumflextilde = '\u1EC4'; + public char Ecyrillic = '\u0404'; + public char Edblgrave = '\u0204'; + public char Edieresis = '\u00CB'; + public char Edieresissmall = '\uF7EB'; + public char Edot = '\u0116'; + public char Edotaccent = '\u0116'; + public char Edotbelow = '\u1EB8'; + public char Efcyrillic = '\u0424'; + public char Egrave = '\u00C8'; + public char Egravesmall = '\uF7E8'; + public char Eharmenian = '\u0537'; + public char Ehookabove = '\u1EBA'; + public char Eightroman = '\u2167'; + public char Einvertedbreve = '\u0206'; + public char Eiotifiedcyrillic = '\u0464'; + public char Elcyrillic = '\u041B'; + public char Elevenroman = '\u216A'; + public char Emacron = '\u0112'; + public char Emacronacute = '\u1E16'; + public char Emacrongrave = '\u1E14'; + public char Emcyrillic = '\u041C'; + public char Emonospace = '\uFF25'; + public char Encyrillic = '\u041D'; + public char Endescendercyrillic = '\u04A2'; + public char Eng = '\u014A'; + public char Enghecyrillic = '\u04A4'; + public char Enhookcyrillic = '\u04C7'; + public char Eogonek = '\u0118'; + public char Eopen = '\u0190'; + public char Epsilon = '\u0395'; + public char Epsilontonos = '\u0388'; + public char Ercyrillic = '\u0420'; + public char Ereversed = '\u018E'; + public char Ereversedcyrillic = '\u042D'; + public char Escyrillic = '\u0421'; + public char Esdescendercyrillic = '\u04AA'; + public char Esh = '\u01A9'; + public char Esmall = '\uF765'; + public char Eta = '\u0397'; + public char Etarmenian = '\u0538'; + public char Etatonos = '\u0389'; + public char Eth = '\u00D0'; + public char Ethsmall = '\uF7F0'; + public char Etilde = '\u1EBC'; + public char Etildebelow = '\u1E1A'; + public char Euro = '\u20AC'; + public char Ezh = '\u01B7'; + public char Ezhcaron = '\u01EE'; + public char Ezhreversed = '\u01B8'; + public char F = '\u0046'; + public char Fcircle = '\u24BB'; + public char Fdotaccent = '\u1E1E'; + public char Feharmenian = '\u0556'; + public char Feicoptic = '\u03E4'; + public char Fhook = '\u0191'; + public char Fitacyrillic = '\u0472'; + public char Fiveroman = '\u2164'; + public char Fmonospace = '\uFF26'; + public char Fourroman = '\u2163'; + public char Fsmall = '\uF766'; + public char G = '\u0047'; + public char GBsquare = '\u3387'; + public char Gacute = '\u01F4'; + public char Gamma = '\u0393'; + public char Gammaafrican = '\u0194'; + public char Gangiacoptic = '\u03EA'; + public char Gbreve = '\u011E'; + public char Gcaron = '\u01E6'; + public char Gcedilla = '\u0122'; + public char Gcircle = '\u24BC'; + public char Gcircumflex = '\u011C'; + public char Gcommaaccent = '\u0122'; + public char Gdot = '\u0120'; + public char Gdotaccent = '\u0120'; + public char Gecyrillic = '\u0413'; + public char Ghadarmenian = '\u0542'; + public char Ghemiddlehookcyrillic = '\u0494'; + public char Ghestrokecyrillic = '\u0492'; + public char Gheupturncyrillic = '\u0490'; + public char Ghook = '\u0193'; + public char Gimarmenian = '\u0533'; + public char Gjecyrillic = '\u0403'; + public char Gmacron = '\u1E20'; + public char Gmonospace = '\uFF27'; + public char Grave = '\uF6CE'; + public char Gravesmall = '\uF760'; + public char Gsmall = '\uF767'; + public char Gsmallhook = '\u029B'; + public char Gstroke = '\u01E4'; + public char H = '\u0048'; + public char H18533 = '\u25CF'; + public char H18543 = '\u25AA'; + public char H18551 = '\u25AB'; + public char H22073 = '\u25A1'; + public char HPsquare = '\u33CB'; + public char Haabkhasiancyrillic = '\u04A8'; + public char Hadescendercyrillic = '\u04B2'; + public char Hardsigncyrillic = '\u042A'; + public char Hbar = '\u0126'; + public char Hbrevebelow = '\u1E2A'; + public char Hcedilla = '\u1E28'; + public char Hcircle = '\u24BD'; + public char Hcircumflex = '\u0124'; + public char Hdieresis = '\u1E26'; + public char Hdotaccent = '\u1E22'; + public char Hdotbelow = '\u1E24'; + public char Hmonospace = '\uFF28'; + public char Hoarmenian = '\u0540'; + public char Horicoptic = '\u03E8'; + public char Hsmall = '\uF768'; + public char Hungarumlaut = '\uF6CF'; + public char Hungarumlautsmall = '\uF6F8'; + public char Hzsquare = '\u3390'; + public char I = '\u0049'; + public char IAcyrillic = '\u042F'; + public char IJ = '\u0132'; + public char IUcyrillic = '\u042E'; + public char Iacute = '\u00CD'; + public char Iacutesmall = '\uF7ED'; + public char Ibreve = '\u012C'; + public char Icaron = '\u01CF'; + public char Icircle = '\u24BE'; + public char Icircumflex = '\u00CE'; + public char Icircumflexsmall = '\uF7EE'; + public char Icyrillic = '\u0406'; + public char Idblgrave = '\u0208'; + public char Idieresis = '\u00CF'; + public char Idieresisacute = '\u1E2E'; + public char Idieresiscyrillic = '\u04E4'; + public char Idieresissmall = '\uF7EF'; + public char Idot = '\u0130'; + public char Idotaccent = '\u0130'; + public char Idotbelow = '\u1ECA'; + public char Iebrevecyrillic = '\u04D6'; + public char Iecyrillic = '\u0415'; + public char Ifraktur = '\u2111'; + public char Igrave = '\u00CC'; + public char Igravesmall = '\uF7EC'; + public char Ihookabove = '\u1EC8'; + public char Iicyrillic = '\u0418'; + public char Iinvertedbreve = '\u020A'; + public char Iishortcyrillic = '\u0419'; + public char Imacron = '\u012A'; + public char Imacroncyrillic = '\u04E2'; + public char Imonospace = '\uFF29'; + public char Iniarmenian = '\u053B'; + public char Iocyrillic = '\u0401'; + public char Iogonek = '\u012E'; + public char Iota = '\u0399'; + public char Iotaafrican = '\u0196'; + public char Iotadieresis = '\u03AA'; + public char Iotatonos = '\u038A'; + public char Ismall = '\uF769'; + public char Istroke = '\u0197'; + public char Itilde = '\u0128'; + public char Itildebelow = '\u1E2C'; + public char Izhitsacyrillic = '\u0474'; + public char Izhitsadblgravecyrillic = '\u0476'; + public char J = '\u004A'; + public char Jaarmenian = '\u0541'; + public char Jcircle = '\u24BF'; + public char Jcircumflex = '\u0134'; + public char Jecyrillic = '\u0408'; + public char Jheharmenian = '\u054B'; + public char Jmonospace = '\uFF2A'; + public char Jsmall = '\uF76A'; + public char K = '\u004B'; + public char KBsquare = '\u3385'; + public char KKsquare = '\u33CD'; + public char Kabashkircyrillic = '\u04A0'; + public char Kacute = '\u1E30'; + public char Kacyrillic = '\u041A'; + public char Kadescendercyrillic = '\u049A'; + public char Kahookcyrillic = '\u04C3'; + public char Kappa = '\u039A'; + public char Kastrokecyrillic = '\u049E'; + public char Kaverticalstrokecyrillic = '\u049C'; + public char Kcaron = '\u01E8'; + public char Kcedilla = '\u0136'; + public char Kcircle = '\u24C0'; + public char Kcommaaccent = '\u0136'; + public char Kdotbelow = '\u1E32'; + public char Keharmenian = '\u0554'; + public char Kenarmenian = '\u053F'; + public char Khacyrillic = '\u0425'; + public char Kheicoptic = '\u03E6'; + public char Khook = '\u0198'; + public char Kjecyrillic = '\u040C'; + public char Klinebelow = '\u1E34'; + public char Kmonospace = '\uFF2B'; + public char Koppacyrillic = '\u0480'; + public char Koppagreek = '\u03DE'; + public char Ksicyrillic = '\u046E'; + public char Ksmall = '\uF76B'; + public char L = '\u004C'; + public char LJ = '\u01C7'; + public char LL = '\uF6BF'; + public char Lacute = '\u0139'; + public char Lambda = '\u039B'; + public char Lcaron = '\u013D'; + public char Lcedilla = '\u013B'; + public char Lcircle = '\u24C1'; + public char Lcircumflexbelow = '\u1E3C'; + public char Lcommaaccent = '\u013B'; + public char Ldot = '\u013F'; + public char Ldotaccent = '\u013F'; + public char Ldotbelow = '\u1E36'; + public char Ldotbelowmacron = '\u1E38'; + public char Liwnarmenian = '\u053C'; + public char Lj = '\u01C8'; + public char Ljecyrillic = '\u0409'; + public char Llinebelow = '\u1E3A'; + public char Lmonospace = '\uFF2C'; + public char Lslash = '\u0141'; + public char Lslashsmall = '\uF6F9'; + public char Lsmall = '\uF76C'; + public char M = '\u004D'; + public char MBsquare = '\u3386'; + public char Macron = '\uF6D0'; + public char Macronsmall = '\uF7AF'; + public char Macute = '\u1E3E'; + public char Mcircle = '\u24C2'; + public char Mdotaccent = '\u1E40'; + public char Mdotbelow = '\u1E42'; + public char Menarmenian = '\u0544'; + public char Mmonospace = '\uFF2D'; + public char Msmall = '\uF76D'; + public char Mturned = '\u019C'; + public char Mu = '\u039C'; + public char N = '\u004E'; + public char NJ = '\u01CA'; + public char Nacute = '\u0143'; + public char Ncaron = '\u0147'; + public char Ncedilla = '\u0145'; + public char Ncircle = '\u24C3'; + public char Ncircumflexbelow = '\u1E4A'; + public char Ncommaaccent = '\u0145'; + public char Ndotaccent = '\u1E44'; + public char Ndotbelow = '\u1E46'; + public char Nhookleft = '\u019D'; + public char Nineroman = '\u2168'; + public char Nj = '\u01CB'; + public char Njecyrillic = '\u040A'; + public char Nlinebelow = '\u1E48'; + public char Nmonospace = '\uFF2E'; + public char Nowarmenian = '\u0546'; + public char Nsmall = '\uF76E'; + public char Ntilde = '\u00D1'; + public char Ntildesmall = '\uF7F1'; + public char Nu = '\u039D'; + public char O = '\u004F'; + public char OE = '\u0152'; + public char OEsmall = '\uF6FA'; + public char Oacute = '\u00D3'; + public char Oacutesmall = '\uF7F3'; +//Obarredcyrillic;04E8 +//Obarreddieresiscyrillic;04EA +//Obreve;014E +//Ocaron;01D1 +//Ocenteredtilde;019F +//Ocircle;24C4 +//Ocircumflex;00D4 +//Ocircumflexacute;1ED0 +//Ocircumflexdotbelow;1ED8 +//Ocircumflexgrave;1ED2 +//Ocircumflexhookabove;1ED4 +//Ocircumflexsmall;F7F4 +//Ocircumflextilde;1ED6 +//Ocyrillic;041E +//Odblacute;0150 +//Odblgrave;020C +//Odieresis;00D6 +//Odieresiscyrillic;04E6 +//Odieresissmall;F7F6 +//Odotbelow;1ECC +//Ogoneksmall;F6FB +//Ograve;00D2 +//Ogravesmall;F7F2 +//Oharmenian;0555 +//Ohm;2126 +//Ohookabove;1ECE +//Ohorn;01A0 +//Ohornacute;1EDA +//Ohorndotbelow;1EE2 +//Ohorngrave;1EDC +//Ohornhookabove;1EDE +//Ohorntilde;1EE0 +//Ohungarumlaut;0150 +//Oi;01A2 +//Oinvertedbreve;020E +//Omacron;014C +//Omacronacute;1E52 +//Omacrongrave;1E50 +//Omega;2126 +//Omegacyrillic;0460 +//Omegagreek;03A9 +//Omegaroundcyrillic;047A +//Omegatitlocyrillic;047C +//Omegatonos;038F +//Omicron;039F +//Omicrontonos;038C +//Omonospace;FF2F +//Oneroman;2160 +//Oogonek;01EA +//Oogonekmacron;01EC +//Oopen;0186 +//Oslash;00D8 +//Oslashacute;01FE +//Oslashsmall;F7F8 +//Osmall;F76F +//Ostrokeacute;01FE +//Otcyrillic;047E +//Otilde;00D5 +//Otildeacute;1E4C +//Otildedieresis;1E4E +//Otildesmall;F7F5 +//P;0050 +//Pacute;1E54 +//Pcircle;24C5 +//Pdotaccent;1E56 +//Pecyrillic;041F +//Peharmenian;054A +//Pemiddlehookcyrillic;04A6 +//Phi;03A6 +//Phook;01A4 +//Pi;03A0 +//Piwrarmenian;0553 +//Pmonospace;FF30 +//Psi;03A8 +//Psicyrillic;0470 +//Psmall;F770 +//Q;0051 +//Qcircle;24C6 +//Qmonospace;FF31 +//Qsmall;F771 +//R;0052 +//Raarmenian;054C +//Racute;0154 +//Rcaron;0158 +//Rcedilla;0156 +//Rcircle;24C7 +//Rcommaaccent;0156 +//Rdblgrave;0210 +//Rdotaccent;1E58 +//Rdotbelow;1E5A +//Rdotbelowmacron;1E5C +//Reharmenian;0550 +//Rfraktur;211C +//Rho;03A1 +//Ringsmall;F6FC +//Rinvertedbreve;0212 +//Rlinebelow;1E5E +//Rmonospace;FF32 +//Rsmall;F772 +//Rsmallinverted;0281 +//Rsmallinvertedsuperior;02B6 +//S;0053 +//SF010000;250C +//SF020000;2514 +//SF030000;2510 +//SF040000;2518 +//SF050000;253C +//SF060000;252C +//SF070000;2534 +//SF080000;251C +//SF090000;2524 +//SF100000;2500 +//SF110000;2502 +//SF190000;2561 +//SF200000;2562 +//SF210000;2556 +//SF220000;2555 +//SF230000;2563 +//SF240000;2551 +//SF250000;2557 +//SF260000;255D +//SF270000;255C +//SF280000;255B +//SF360000;255E +//SF370000;255F +//SF380000;255A +//SF390000;2554 +//SF400000;2569 +//SF410000;2566 +//SF420000;2560 +//SF430000;2550 +//SF440000;256C +//SF450000;2567 +//SF460000;2568 +//SF470000;2564 +//SF480000;2565 +//SF490000;2559 +//SF500000;2558 +//SF510000;2552 +//SF520000;2553 +//SF530000;256B +//SF540000;256A +//Sacute;015A +//Sacutedotaccent;1E64 +//Sampigreek;03E0 +//Scaron;0160 +//Scarondotaccent;1E66 +//Scaronsmall;F6FD +//Scedilla;015E +//Schwa;018F +//Schwacyrillic;04D8 +//Schwadieresiscyrillic;04DA +//Scircle;24C8 +//Scircumflex;015C +//Scommaaccent;0218 +//Sdotaccent;1E60 +//Sdotbelow;1E62 +//Sdotbelowdotaccent;1E68 +//Seharmenian;054D +//Sevenroman;2166 +//Shaarmenian;0547 +//Shacyrillic;0428 +//Shchacyrillic;0429 +//Sheicoptic;03E2 +//Shhacyrillic;04BA +//Shimacoptic;03EC +//Sigma;03A3 +//Sixroman;2165 +//Smonospace;FF33 +//Softsigncyrillic;042C +//Ssmall;F773 +//Stigmagreek;03DA +//T;0054 +//Tau;03A4 +//Tbar;0166 +//Tcaron;0164 +//Tcedilla;0162 +//Tcircle;24C9 +//Tcircumflexbelow;1E70 +//Tcommaaccent;0162 +//Tdotaccent;1E6A +//Tdotbelow;1E6C +//Tecyrillic;0422 +//Tedescendercyrillic;04AC +//Tenroman;2169 +//Tetsecyrillic;04B4 +//Theta;0398 +//Thook;01AC +//Thorn;00DE +//Thornsmall;F7FE +//Threeroman;2162 +//Tildesmall;F6FE +//Tiwnarmenian;054F +//Tlinebelow;1E6E +//Tmonospace;FF34 +//Toarmenian;0539 +//Tonefive;01BC +//Tonesix;0184 +//Tonetwo;01A7 +//Tretroflexhook;01AE +//Tsecyrillic;0426 +//Tshecyrillic;040B +//Tsmall;F774 +//Twelveroman;216B +//Tworoman;2161 +//U;0055 +//Uacute;00DA +//Uacutesmall;F7FA +//Ubreve;016C +//Ucaron;01D3 +//Ucircle;24CA +//Ucircumflex;00DB +//Ucircumflexbelow;1E76 +//Ucircumflexsmall;F7FB +//Ucyrillic;0423 +//Udblacute;0170 +//Udblgrave;0214 +//Udieresis;00DC +//Udieresisacute;01D7 +//Udieresisbelow;1E72 +//Udieresiscaron;01D9 +//Udieresiscyrillic;04F0 +//Udieresisgrave;01DB +//Udieresismacron;01D5 +//Udieresissmall;F7FC +//Udotbelow;1EE4 +//Ugrave;00D9 +//Ugravesmall;F7F9 +//Uhookabove;1EE6 +//Uhorn;01AF +//Uhornacute;1EE8 +//Uhorndotbelow;1EF0 +//Uhorngrave;1EEA +//Uhornhookabove;1EEC +//Uhorntilde;1EEE +//Uhungarumlaut;0170 +//Uhungarumlautcyrillic;04F2 +//Uinvertedbreve;0216 +//Ukcyrillic;0478 +//Umacron;016A +//Umacroncyrillic;04EE +//Umacrondieresis;1E7A +//Umonospace;FF35 +//Uogonek;0172 +//Upsilon;03A5 +//Upsilon1;03D2 +//Upsilonacutehooksymbolgreek;03D3 +//Upsilonafrican;01B1 +//Upsilondieresis;03AB +//Upsilondieresishooksymbolgreek;03D4 +//Upsilonhooksymbol;03D2 +//Upsilontonos;038E +//Uring;016E +//Ushortcyrillic;040E +//Usmall;F775 +//Ustraightcyrillic;04AE +//Ustraightstrokecyrillic;04B0 +//Utilde;0168 +//Utildeacute;1E78 +//Utildebelow;1E74 +//V;0056 +//Vcircle;24CB +//Vdotbelow;1E7E +//Vecyrillic;0412 +//Vewarmenian;054E +//Vhook;01B2 +//Vmonospace;FF36 +//Voarmenian;0548 +//Vsmall;F776 +//Vtilde;1E7C +//W;0057 +//Wacute;1E82 +//Wcircle;24CC +//Wcircumflex;0174 +//Wdieresis;1E84 +//Wdotaccent;1E86 +//Wdotbelow;1E88 +//Wgrave;1E80 +//Wmonospace;FF37 +//Wsmall;F777 +//X;0058 +//Xcircle;24CD +//Xdieresis;1E8C +//Xdotaccent;1E8A +//Xeharmenian;053D +//Xi;039E +//Xmonospace;FF38 +//Xsmall;F778 +//Y;0059 +//Yacute;00DD +//Yacutesmall;F7FD +//Yatcyrillic;0462 +//Ycircle;24CE +//Ycircumflex;0176 +//Ydieresis;0178 +//Ydieresissmall;F7FF +//Ydotaccent;1E8E +//Ydotbelow;1EF4 +//Yericyrillic;042B +//Yerudieresiscyrillic;04F8 +//Ygrave;1EF2 +//Yhook;01B3 +//Yhookabove;1EF6 +//Yiarmenian;0545 +//Yicyrillic;0407 +//Yiwnarmenian;0552 +//Ymonospace;FF39 +//Ysmall;F779 +//Ytilde;1EF8 +//Yusbigcyrillic;046A +//Yusbigiotifiedcyrillic;046C +//Yuslittlecyrillic;0466 +//Yuslittleiotifiedcyrillic;0468 +//Z;005A +//Zaarmenian;0536 +//Zacute;0179 +//Zcaron;017D +//Zcaronsmall;F6FF +//Zcircle;24CF +//Zcircumflex;1E90 +//Zdot;017B +//Zdotaccent;017B +//Zdotbelow;1E92 +//Zecyrillic;0417 +//Zedescendercyrillic;0498 +//Zedieresiscyrillic;04DE +//Zeta;0396 +//Zhearmenian;053A +//Zhebrevecyrillic;04C1 +//Zhecyrillic;0416 +//Zhedescendercyrillic;0496 +//Zhedieresiscyrillic;04DC +//Zlinebelow;1E94 +//Zmonospace;FF3A +//Zsmall;F77A +//Zstroke;01B5 +//a;0061 +//aabengali;0986 +//aacute;00E1 +//aadeva;0906 +//aagujarati;0A86 +//aagurmukhi;0A06 +//aamatragurmukhi;0A3E +//aarusquare;3303 +//aavowelsignbengali;09BE +//aavowelsigndeva;093E +//aavowelsigngujarati;0ABE +//abbreviationmarkarmenian;055F +//abbreviationsigndeva;0970 +//abengali;0985 +//abopomofo;311A +//abreve;0103 +//abreveacute;1EAF +//abrevecyrillic;04D1 +//abrevedotbelow;1EB7 +//abrevegrave;1EB1 +//abrevehookabove;1EB3 +//abrevetilde;1EB5 +//acaron;01CE +//acircle;24D0 +//acircumflex;00E2 +//acircumflexacute;1EA5 +//acircumflexdotbelow;1EAD +//acircumflexgrave;1EA7 +//acircumflexhookabove;1EA9 +//acircumflextilde;1EAB +//acute;00B4 +//acutebelowcmb;0317 +//acutecmb;0301 +//acutecomb;0301 +//acutedeva;0954 +//acutelowmod;02CF +//acutetonecmb;0341 +//acyrillic;0430 +//adblgrave;0201 +//addakgurmukhi;0A71 +//adeva;0905 +//adieresis;00E4 +//adieresiscyrillic;04D3 +//adieresismacron;01DF +//adotbelow;1EA1 +//adotmacron;01E1 +//ae;00E6 +//aeacute;01FD +//aekorean;3150 +//aemacron;01E3 +//afii00208;2015 +//afii08941;20A4 +//afii10017;0410 +//afii10018;0411 +//afii10019;0412 +//afii10020;0413 +//afii10021;0414 +//afii10022;0415 +//afii10023;0401 +//afii10024;0416 +//afii10025;0417 +//afii10026;0418 +//afii10027;0419 +//afii10028;041A +//afii10029;041B +//afii10030;041C +//afii10031;041D +//afii10032;041E +//afii10033;041F +//afii10034;0420 +//afii10035;0421 +//afii10036;0422 +//afii10037;0423 +//afii10038;0424 +//afii10039;0425 +//afii10040;0426 +//afii10041;0427 +//afii10042;0428 +//afii10043;0429 +//afii10044;042A +//afii10045;042B +//afii10046;042C +//afii10047;042D +//afii10048;042E +//afii10049;042F +//afii10050;0490 +//afii10051;0402 +//afii10052;0403 +//afii10053;0404 +//afii10054;0405 +//afii10055;0406 +//afii10056;0407 +//afii10057;0408 +//afii10058;0409 +//afii10059;040A +//afii10060;040B +//afii10061;040C +//afii10062;040E +//afii10063;F6C4 +//afii10064;F6C5 +//afii10065;0430 +//afii10066;0431 +//afii10067;0432 +//afii10068;0433 +//afii10069;0434 +//afii10070;0435 +//afii10071;0451 +//afii10072;0436 +//afii10073;0437 +//afii10074;0438 +//afii10075;0439 +//afii10076;043A +//afii10077;043B +//afii10078;043C +//afii10079;043D +//afii10080;043E +//afii10081;043F +//afii10082;0440 +//afii10083;0441 +//afii10084;0442 +//afii10085;0443 +//afii10086;0444 +//afii10087;0445 +//afii10088;0446 +//afii10089;0447 +//afii10090;0448 +//afii10091;0449 +//afii10092;044A +//afii10093;044B +//afii10094;044C +//afii10095;044D +//afii10096;044E +//afii10097;044F +//afii10098;0491 +//afii10099;0452 +//afii10100;0453 +//afii10101;0454 +//afii10102;0455 +//afii10103;0456 +//afii10104;0457 +//afii10105;0458 +//afii10106;0459 +//afii10107;045A +//afii10108;045B +//afii10109;045C +//afii10110;045E +//afii10145;040F +//afii10146;0462 +//afii10147;0472 +//afii10148;0474 +//afii10192;F6C6 +//afii10193;045F +//afii10194;0463 +//afii10195;0473 +//afii10196;0475 +//afii10831;F6C7 +//afii10832;F6C8 +//afii10846;04D9 +//afii299;200E +//afii300;200F +//afii301;200D +//afii57381;066A +//afii57388;060C +//afii57392;0660 +//afii57393;0661 +//afii57394;0662 +//afii57395;0663 +//afii57396;0664 +//afii57397;0665 +//afii57398;0666 +//afii57399;0667 +//afii57400;0668 +//afii57401;0669 +//afii57403;061B +//afii57407;061F +//afii57409;0621 +//afii57410;0622 +//afii57411;0623 +//afii57412;0624 +//afii57413;0625 +//afii57414;0626 +//afii57415;0627 +//afii57416;0628 +//afii57417;0629 +//afii57418;062A +//afii57419;062B +//afii57420;062C +//afii57421;062D +//afii57422;062E +//afii57423;062F +//afii57424;0630 +//afii57425;0631 +//afii57426;0632 +//afii57427;0633 +//afii57428;0634 +//afii57429;0635 +//afii57430;0636 +//afii57431;0637 +//afii57432;0638 +//afii57433;0639 +//afii57434;063A +//afii57440;0640 +//afii57441;0641 +//afii57442;0642 +//afii57443;0643 +//afii57444;0644 +//afii57445;0645 +//afii57446;0646 +//afii57448;0648 +//afii57449;0649 +//afii57450;064A +//afii57451;064B +//afii57452;064C +//afii57453;064D +//afii57454;064E +//afii57455;064F +//afii57456;0650 +//afii57457;0651 +//afii57458;0652 +//afii57470;0647 +//afii57505;06A4 +//afii57506;067E +//afii57507;0686 +//afii57508;0698 +//afii57509;06AF +//afii57511;0679 +//afii57512;0688 +//afii57513;0691 +//afii57514;06BA +//afii57519;06D2 +//afii57534;06D5 +//afii57636;20AA +//afii57645;05BE +//afii57658;05C3 +//afii57664;05D0 +//afii57665;05D1 +//afii57666;05D2 +//afii57667;05D3 +//afii57668;05D4 +//afii57669;05D5 +//afii57670;05D6 +//afii57671;05D7 +//afii57672;05D8 +//afii57673;05D9 +//afii57674;05DA +//afii57675;05DB +//afii57676;05DC +//afii57677;05DD +//afii57678;05DE +//afii57679;05DF +//afii57680;05E0 +//afii57681;05E1 +//afii57682;05E2 +//afii57683;05E3 +//afii57684;05E4 +//afii57685;05E5 +//afii57686;05E6 +//afii57687;05E7 +//afii57688;05E8 +//afii57689;05E9 +//afii57690;05EA +//afii57694;FB2A +//afii57695;FB2B +//afii57700;FB4B +//afii57705;FB1F +//afii57716;05F0 +//afii57717;05F1 +//afii57718;05F2 +//afii57723;FB35 +//afii57793;05B4 +//afii57794;05B5 +//afii57795;05B6 +//afii57796;05BB +//afii57797;05B8 +//afii57798;05B7 +//afii57799;05B0 +//afii57800;05B2 +//afii57801;05B1 +//afii57802;05B3 +//afii57803;05C2 +//afii57804;05C1 +//afii57806;05B9 +//afii57807;05BC +//afii57839;05BD +//afii57841;05BF +//afii57842;05C0 +//afii57929;02BC +//afii61248;2105 +//afii61289;2113 +//afii61352;2116 +//afii61573;202C +//afii61574;202D +//afii61575;202E +//afii61664;200C +//afii63167;066D +//afii64937;02BD +//agrave;00E0 +//agujarati;0A85 +//agurmukhi;0A05 +//ahiragana;3042 +//ahookabove;1EA3 +//aibengali;0990 +//aibopomofo;311E +//aideva;0910 +//aiecyrillic;04D5 +//aigujarati;0A90 +//aigurmukhi;0A10 +//aimatragurmukhi;0A48 +//ainarabic;0639 +//ainfinalarabic;FECA +//aininitialarabic;FECB +//ainmedialarabic;FECC +//ainvertedbreve;0203 +//aivowelsignbengali;09C8 +//aivowelsigndeva;0948 +//aivowelsigngujarati;0AC8 +//akatakana;30A2 +//akatakanahalfwidth;FF71 +//akorean;314F +//alef;05D0 +//alefarabic;0627 +//alefdageshhebrew;FB30 +//aleffinalarabic;FE8E +//alefhamzaabovearabic;0623 +//alefhamzaabovefinalarabic;FE84 +//alefhamzabelowarabic;0625 +//alefhamzabelowfinalarabic;FE88 +//alefhebrew;05D0 +//aleflamedhebrew;FB4F +//alefmaddaabovearabic;0622 +//alefmaddaabovefinalarabic;FE82 +//alefmaksuraarabic;0649 +//alefmaksurafinalarabic;FEF0 +//alefmaksurainitialarabic;FEF3 +//alefmaksuramedialarabic;FEF4 +//alefpatahhebrew;FB2E +//alefqamatshebrew;FB2F +//aleph;2135 +//allequal;224C +//alpha;03B1 +//alphatonos;03AC +//amacron;0101 +//amonospace;FF41 +//ampersand;0026 +//ampersandmonospace;FF06 +//ampersandsmall;F726 +//amsquare;33C2 +//anbopomofo;3122 +//angbopomofo;3124 +//angkhankhuthai;0E5A +//angle;2220 +//anglebracketleft;3008 +//anglebracketleftvertical;FE3F +//anglebracketright;3009 +//anglebracketrightvertical;FE40 +//angleleft;2329 +//angleright;232A +//angstrom;212B +//anoteleia;0387 +//anudattadeva;0952 +//anusvarabengali;0982 +//anusvaradeva;0902 +//anusvaragujarati;0A82 +//aogonek;0105 +//apaatosquare;3300 +//aparen;249C +//apostrophearmenian;055A +//apostrophemod;02BC +//apple;F8FF +//approaches;2250 +//approxequal;2248 +//approxequalorimage;2252 +//approximatelyequal;2245 +//araeaekorean;318E +//araeakorean;318D +//arc;2312 +//arighthalfring;1E9A +//aring;00E5 +//aringacute;01FB +//aringbelow;1E01 +//arrowboth;2194 +//arrowdashdown;21E3 +//arrowdashleft;21E0 +//arrowdashright;21E2 +//arrowdashup;21E1 +//arrowdblboth;21D4 +//arrowdbldown;21D3 +//arrowdblleft;21D0 +//arrowdblright;21D2 +//arrowdblup;21D1 +//arrowdown;2193 +//arrowdownleft;2199 +//arrowdownright;2198 +//arrowdownwhite;21E9 +//arrowheaddownmod;02C5 +//arrowheadleftmod;02C2 +//arrowheadrightmod;02C3 +//arrowheadupmod;02C4 +//arrowhorizex;F8E7 +//arrowleft;2190 +//arrowleftdbl;21D0 +//arrowleftdblstroke;21CD +//arrowleftoverright;21C6 +//arrowleftwhite;21E6 +//arrowright;2192 +//arrowrightdblstroke;21CF +//arrowrightheavy;279E +//arrowrightoverleft;21C4 +//arrowrightwhite;21E8 +//arrowtableft;21E4 +//arrowtabright;21E5 +//arrowup;2191 +//arrowupdn;2195 +//arrowupdnbse;21A8 +//arrowupdownbase;21A8 +//arrowupleft;2196 +//arrowupleftofdown;21C5 +//arrowupright;2197 +//arrowupwhite;21E7 +//arrowvertex;F8E6 +//asciicircum;005E +//asciicircummonospace;FF3E +//asciitilde;007E +//asciitildemonospace;FF5E +//ascript;0251 +//ascriptturned;0252 +//asmallhiragana;3041 +//asmallkatakana;30A1 +//asmallkatakanahalfwidth;FF67 +//asterisk;002A +//asteriskaltonearabic;066D +//asteriskarabic;066D +//asteriskmath;2217 +//asteriskmonospace;FF0A +//asterisksmall;FE61 +//asterism;2042 +//asuperior;F6E9 +//asymptoticallyequal;2243 +//at;0040 +//atilde;00E3 +//atmonospace;FF20 +//atsmall;FE6B +//aturned;0250 +//aubengali;0994 +//aubopomofo;3120 +//audeva;0914 +//augujarati;0A94 +//augurmukhi;0A14 +//aulengthmarkbengali;09D7 +//aumatragurmukhi;0A4C +//auvowelsignbengali;09CC +//auvowelsigndeva;094C +//auvowelsigngujarati;0ACC +//avagrahadeva;093D +//aybarmenian;0561 +//ayin;05E2 +//ayinaltonehebrew;FB20 +//ayinhebrew;05E2 +//b;0062 +//babengali;09AC +//backslash;005C +//backslashmonospace;FF3C +//badeva;092C +//bagujarati;0AAC +//bagurmukhi;0A2C +//bahiragana;3070 +//bahtthai;0E3F +//bakatakana;30D0 +//bar;007C +//barmonospace;FF5C +//bbopomofo;3105 +//bcircle;24D1 +//bdotaccent;1E03 +//bdotbelow;1E05 +//beamedsixteenthnotes;266C +//because;2235 +//becyrillic;0431 +//beharabic;0628 +//behfinalarabic;FE90 +//behinitialarabic;FE91 +//behiragana;3079 +//behmedialarabic;FE92 +//behmeeminitialarabic;FC9F +//behmeemisolatedarabic;FC08 +//behnoonfinalarabic;FC6D +//bekatakana;30D9 +//benarmenian;0562 +//bet;05D1 +//beta;03B2 +//betasymbolgreek;03D0 +//betdagesh;FB31 +//betdageshhebrew;FB31 +//bethebrew;05D1 +//betrafehebrew;FB4C +//bhabengali;09AD +//bhadeva;092D +//bhagujarati;0AAD +//bhagurmukhi;0A2D +//bhook;0253 +//bihiragana;3073 +//bikatakana;30D3 +//bilabialclick;0298 +//bindigurmukhi;0A02 +//birusquare;3331 +//blackcircle;25CF +//blackdiamond;25C6 +//blackdownpointingtriangle;25BC +//blackleftpointingpointer;25C4 +//blackleftpointingtriangle;25C0 +//blacklenticularbracketleft;3010 +//blacklenticularbracketleftvertical;FE3B +//blacklenticularbracketright;3011 +//blacklenticularbracketrightvertical;FE3C +//blacklowerlefttriangle;25E3 +//blacklowerrighttriangle;25E2 +//blackrectangle;25AC +//blackrightpointingpointer;25BA +//blackrightpointingtriangle;25B6 +//blacksmallsquare;25AA +//blacksmilingface;263B +//blacksquare;25A0 +//blackstar;2605 +//blackupperlefttriangle;25E4 +//blackupperrighttriangle;25E5 +//blackuppointingsmalltriangle;25B4 +//blackuppointingtriangle;25B2 +//blank;2423 +//blinebelow;1E07 +//block;2588 +//bmonospace;FF42 +//bobaimaithai;0E1A +//bohiragana;307C +//bokatakana;30DC +//bparen;249D +//bqsquare;33C3 +//braceex;F8F4 +//braceleft;007B +//braceleftbt;F8F3 +//braceleftmid;F8F2 +//braceleftmonospace;FF5B +//braceleftsmall;FE5B +//bracelefttp;F8F1 +//braceleftvertical;FE37 +//braceright;007D +//bracerightbt;F8FE +//bracerightmid;F8FD +//bracerightmonospace;FF5D +//bracerightsmall;FE5C +//bracerighttp;F8FC +//bracerightvertical;FE38 +//bracketleft;005B +//bracketleftbt;F8F0 +//bracketleftex;F8EF +//bracketleftmonospace;FF3B +//bracketlefttp;F8EE +//bracketright;005D +//bracketrightbt;F8FB +//bracketrightex;F8FA +//bracketrightmonospace;FF3D +//bracketrighttp;F8F9 +//breve;02D8 +//brevebelowcmb;032E +//brevecmb;0306 +//breveinvertedbelowcmb;032F +//breveinvertedcmb;0311 +//breveinverteddoublecmb;0361 +//bridgebelowcmb;032A +//bridgeinvertedbelowcmb;033A +//brokenbar;00A6 +//bstroke;0180 +//bsuperior;F6EA +//btopbar;0183 +//buhiragana;3076 +//bukatakana;30D6 +//bullet;2022 +//bulletinverse;25D8 +//bulletoperator;2219 +//bullseye;25CE +//c;0063 +//caarmenian;056E +//cabengali;099A +//cacute;0107 +//cadeva;091A +//cagujarati;0A9A +//cagurmukhi;0A1A +//calsquare;3388 +//candrabindubengali;0981 +//candrabinducmb;0310 +//candrabindudeva;0901 +//candrabindugujarati;0A81 +//capslock;21EA +//careof;2105 +//caron;02C7 +//caronbelowcmb;032C +//caroncmb;030C +//carriagereturn;21B5 +//cbopomofo;3118 +//ccaron;010D +//ccedilla;00E7 +//ccedillaacute;1E09 +//ccircle;24D2 +//ccircumflex;0109 +//ccurl;0255 +//cdot;010B +//cdotaccent;010B +//cdsquare;33C5 +//cedilla;00B8 +//cedillacmb;0327 +//cent;00A2 +//centigrade;2103 +//centinferior;F6DF +//centmonospace;FFE0 +//centoldstyle;F7A2 +//centsuperior;F6E0 +//chaarmenian;0579 +//chabengali;099B +//chadeva;091B +//chagujarati;0A9B +//chagurmukhi;0A1B +//chbopomofo;3114 +//cheabkhasiancyrillic;04BD +//checkmark;2713 +//checyrillic;0447 +//chedescenderabkhasiancyrillic;04BF +//chedescendercyrillic;04B7 +//chedieresiscyrillic;04F5 +//cheharmenian;0573 +//chekhakassiancyrillic;04CC +//cheverticalstrokecyrillic;04B9 +//chi;03C7 +//chieuchacirclekorean;3277 +//chieuchaparenkorean;3217 +//chieuchcirclekorean;3269 +//chieuchkorean;314A +//chieuchparenkorean;3209 +//chochangthai;0E0A +//chochanthai;0E08 +//chochingthai;0E09 +//chochoethai;0E0C +//chook;0188 +//cieucacirclekorean;3276 +//cieucaparenkorean;3216 +//cieuccirclekorean;3268 +//cieuckorean;3148 +//cieucparenkorean;3208 +//cieucuparenkorean;321C +//circle;25CB +//circlemultiply;2297 +//circleot;2299 +//circleplus;2295 +//circlepostalmark;3036 +//circlewithlefthalfblack;25D0 +//circlewithrighthalfblack;25D1 +//circumflex;02C6 +//circumflexbelowcmb;032D +//circumflexcmb;0302 +//clear;2327 +//clickalveolar;01C2 +//clickdental;01C0 +//clicklateral;01C1 +//clickretroflex;01C3 +//club;2663 +//clubsuitblack;2663 +//clubsuitwhite;2667 +//cmcubedsquare;33A4 +//cmonospace;FF43 +//cmsquaredsquare;33A0 +//coarmenian;0581 +//colon;003A +//colonmonetary;20A1 +//colonmonospace;FF1A +//colonsign;20A1 +//colonsmall;FE55 +//colontriangularhalfmod;02D1 +//colontriangularmod;02D0 +//comma;002C +//commaabovecmb;0313 +//commaaboverightcmb;0315 +//commaaccent;F6C3 +//commaarabic;060C +//commaarmenian;055D +//commainferior;F6E1 +//commamonospace;FF0C +//commareversedabovecmb;0314 +//commareversedmod;02BD +//commasmall;FE50 +//commasuperior;F6E2 +//commaturnedabovecmb;0312 +//commaturnedmod;02BB +//compass;263C +//congruent;2245 +//contourintegral;222E +//control;2303 +//controlACK;0006 +//controlBEL;0007 +//controlBS;0008 +//controlCAN;0018 +//controlCR;000D +//controlDC1;0011 +//controlDC2;0012 +//controlDC3;0013 +//controlDC4;0014 +//controlDEL;007F +//controlDLE;0010 +//controlEM;0019 +//controlENQ;0005 +//controlEOT;0004 +//controlESC;001B +//controlETB;0017 +//controlETX;0003 +//controlFF;000C +//controlFS;001C +//controlGS;001D +//controlHT;0009 +//controlLF;000A +//controlNAK;0015 +//controlRS;001E +//controlSI;000F +//controlSO;000E +//controlSOT;0002 +//controlSTX;0001 +//controlSUB;001A +//controlSYN;0016 +//controlUS;001F +//controlVT;000B +//copyright;00A9 +//copyrightsans;F8E9 +//copyrightserif;F6D9 +//cornerbracketleft;300C +//cornerbracketlefthalfwidth;FF62 +//cornerbracketleftvertical;FE41 +//cornerbracketright;300D +//cornerbracketrighthalfwidth;FF63 +//cornerbracketrightvertical;FE42 +//corporationsquare;337F +//cosquare;33C7 +//coverkgsquare;33C6 +//cparen;249E +//cruzeiro;20A2 +//cstretched;0297 +//curlyand;22CF +//curlyor;22CE +//currency;00A4 +//cyrBreve;F6D1 +//cyrFlex;F6D2 +//cyrbreve;F6D4 +//cyrflex;F6D5 +//d;0064 +//daarmenian;0564 +//dabengali;09A6 +//dadarabic;0636 +//dadeva;0926 +//dadfinalarabic;FEBE +//dadinitialarabic;FEBF +//dadmedialarabic;FEC0 +//dagesh;05BC +//dageshhebrew;05BC +//dagger;2020 +//daggerdbl;2021 +//dagujarati;0AA6 +//dagurmukhi;0A26 +//dahiragana;3060 +//dakatakana;30C0 +//dalarabic;062F +//dalet;05D3 +//daletdagesh;FB33 +//daletdageshhebrew;FB33 +//dalethatafpatah;05D3 05B2 +//dalethatafpatahhebrew;05D3 05B2 +//dalethatafsegol;05D3 05B1 +//dalethatafsegolhebrew;05D3 05B1 +//dalethebrew;05D3 +//dalethiriq;05D3 05B4 +//dalethiriqhebrew;05D3 05B4 +//daletholam;05D3 05B9 +//daletholamhebrew;05D3 05B9 +//daletpatah;05D3 05B7 +//daletpatahhebrew;05D3 05B7 +//daletqamats;05D3 05B8 +//daletqamatshebrew;05D3 05B8 +//daletqubuts;05D3 05BB +//daletqubutshebrew;05D3 05BB +//daletsegol;05D3 05B6 +//daletsegolhebrew;05D3 05B6 +//daletsheva;05D3 05B0 +//daletshevahebrew;05D3 05B0 +//dalettsere;05D3 05B5 +//dalettserehebrew;05D3 05B5 +//dalfinalarabic;FEAA +//dammaarabic;064F +//dammalowarabic;064F +//dammatanaltonearabic;064C +//dammatanarabic;064C +//danda;0964 +//dargahebrew;05A7 +//dargalefthebrew;05A7 +//dasiapneumatacyrilliccmb;0485 +//dblGrave;F6D3 +//dblanglebracketleft;300A +//dblanglebracketleftvertical;FE3D +//dblanglebracketright;300B +//dblanglebracketrightvertical;FE3E +//dblarchinvertedbelowcmb;032B +//dblarrowleft;21D4 +//dblarrowright;21D2 +//dbldanda;0965 +//dblgrave;F6D6 +//dblgravecmb;030F +//dblintegral;222C +//dbllowline;2017 +//dbllowlinecmb;0333 +//dbloverlinecmb;033F +//dblprimemod;02BA +//dblverticalbar;2016 +//dblverticallineabovecmb;030E +//dbopomofo;3109 +//dbsquare;33C8 +//dcaron;010F +//dcedilla;1E11 +//dcircle;24D3 +//dcircumflexbelow;1E13 +//dcroat;0111 +//ddabengali;09A1 +//ddadeva;0921 +//ddagujarati;0AA1 +//ddagurmukhi;0A21 +//ddalarabic;0688 +//ddalfinalarabic;FB89 +//dddhadeva;095C +//ddhabengali;09A2 +//ddhadeva;0922 +//ddhagujarati;0AA2 +//ddhagurmukhi;0A22 +//ddotaccent;1E0B +//ddotbelow;1E0D +//decimalseparatorarabic;066B +//decimalseparatorpersian;066B +//decyrillic;0434 +//degree;00B0 +//dehihebrew;05AD +//dehiragana;3067 +//deicoptic;03EF +//dekatakana;30C7 +//deleteleft;232B +//deleteright;2326 +//delta;03B4 +//deltaturned;018D +//denominatorminusonenumeratorbengali;09F8 +//dezh;02A4 +//dhabengali;09A7 +//dhadeva;0927 +//dhagujarati;0AA7 +//dhagurmukhi;0A27 +//dhook;0257 +//dialytikatonos;0385 +//dialytikatonoscmb;0344 +//diamond;2666 +//diamondsuitwhite;2662 +//dieresis;00A8 +//dieresisacute;F6D7 +//dieresisbelowcmb;0324 +//dieresiscmb;0308 +//dieresisgrave;F6D8 +//dieresistonos;0385 +//dihiragana;3062 +//dikatakana;30C2 +//dittomark;3003 +//divide;00F7 +//divides;2223 +//divisionslash;2215 +//djecyrillic;0452 +//dkshade;2593 +//dlinebelow;1E0F +//dlsquare;3397 +//dmacron;0111 +//dmonospace;FF44 +//dnblock;2584 +//dochadathai;0E0E +//dodekthai;0E14 +//dohiragana;3069 +//dokatakana;30C9 +//dollar;0024 +//dollarinferior;F6E3 +//dollarmonospace;FF04 +//dollaroldstyle;F724 +//dollarsmall;FE69 +//dollarsuperior;F6E4 +//dong;20AB +//dorusquare;3326 +//dotaccent;02D9 +//dotaccentcmb;0307 +//dotbelowcmb;0323 +//dotbelowcomb;0323 +//dotkatakana;30FB +//dotlessi;0131 +//dotlessj;F6BE +//dotlessjstrokehook;0284 +//dotmath;22C5 +//dottedcircle;25CC +//doubleyodpatah;FB1F +//doubleyodpatahhebrew;FB1F +//downtackbelowcmb;031E +//downtackmod;02D5 +//dparen;249F +//dsuperior;F6EB +//dtail;0256 +//dtopbar;018C +//duhiragana;3065 +//dukatakana;30C5 +//dz;01F3 +//dzaltone;02A3 +//dzcaron;01C6 +//dzcurl;02A5 +//dzeabkhasiancyrillic;04E1 +//dzecyrillic;0455 +//dzhecyrillic;045F +//e;0065 +//eacute;00E9 +//earth;2641 +//ebengali;098F +//ebopomofo;311C +//ebreve;0115 +//ecandradeva;090D +//ecandragujarati;0A8D +//ecandravowelsigndeva;0945 +//ecandravowelsigngujarati;0AC5 +//ecaron;011B +//ecedillabreve;1E1D +//echarmenian;0565 +//echyiwnarmenian;0587 +//ecircle;24D4 +//ecircumflex;00EA +//ecircumflexacute;1EBF +//ecircumflexbelow;1E19 +//ecircumflexdotbelow;1EC7 +//ecircumflexgrave;1EC1 +//ecircumflexhookabove;1EC3 +//ecircumflextilde;1EC5 +//ecyrillic;0454 +//edblgrave;0205 +//edeva;090F +//edieresis;00EB +//edot;0117 +//edotaccent;0117 +//edotbelow;1EB9 +//eegurmukhi;0A0F +//eematragurmukhi;0A47 +//efcyrillic;0444 +//egrave;00E8 +//egujarati;0A8F +//eharmenian;0567 +//ehbopomofo;311D +//ehiragana;3048 +//ehookabove;1EBB +//eibopomofo;311F +//eight;0038 +//eightarabic;0668 +//eightbengali;09EE +//eightcircle;2467 +//eightcircleinversesansserif;2791 +//eightdeva;096E +//eighteencircle;2471 +//eighteenparen;2485 +//eighteenperiod;2499 +//eightgujarati;0AEE +//eightgurmukhi;0A6E +//eighthackarabic;0668 +//eighthangzhou;3028 +//eighthnotebeamed;266B +//eightideographicparen;3227 +//eightinferior;2088 +//eightmonospace;FF18 +//eightoldstyle;F738 +//eightparen;247B +//eightperiod;248F +//eightpersian;06F8 +//eightroman;2177 +//eightsuperior;2078 +//eightthai;0E58 +//einvertedbreve;0207 +//eiotifiedcyrillic;0465 +//ekatakana;30A8 +//ekatakanahalfwidth;FF74 +//ekonkargurmukhi;0A74 +//ekorean;3154 +//elcyrillic;043B +//element;2208 +//elevencircle;246A +//elevenparen;247E +//elevenperiod;2492 +//elevenroman;217A +//ellipsis;2026 +//ellipsisvertical;22EE +//emacron;0113 +//emacronacute;1E17 +//emacrongrave;1E15 +//emcyrillic;043C +//emdash;2014 +//emdashvertical;FE31 +//emonospace;FF45 +//emphasismarkarmenian;055B +//emptyset;2205 +//enbopomofo;3123 +//encyrillic;043D +//endash;2013 +//endashvertical;FE32 +//endescendercyrillic;04A3 +//eng;014B +//engbopomofo;3125 +//enghecyrillic;04A5 +//enhookcyrillic;04C8 +//enspace;2002 +//eogonek;0119 +//eokorean;3153 +//eopen;025B +//eopenclosed;029A +//eopenreversed;025C +//eopenreversedclosed;025E +//eopenreversedhook;025D +//eparen;24A0 +//epsilon;03B5 +//epsilontonos;03AD +//equal;003D +//equalmonospace;FF1D +//equalsmall;FE66 +//equalsuperior;207C +//equivalence;2261 +//erbopomofo;3126 +//ercyrillic;0440 +//ereversed;0258 +//ereversedcyrillic;044D +//escyrillic;0441 +//esdescendercyrillic;04AB +//esh;0283 +//eshcurl;0286 +//eshortdeva;090E +//eshortvowelsigndeva;0946 +//eshreversedloop;01AA +//eshsquatreversed;0285 +//esmallhiragana;3047 +//esmallkatakana;30A7 +//esmallkatakanahalfwidth;FF6A +//estimated;212E +//esuperior;F6EC +//eta;03B7 +//etarmenian;0568 +//etatonos;03AE +//eth;00F0 +//etilde;1EBD +//etildebelow;1E1B +//etnahtafoukhhebrew;0591 +//etnahtafoukhlefthebrew;0591 +//etnahtahebrew;0591 +//etnahtalefthebrew;0591 +//eturned;01DD +//eukorean;3161 +//euro;20AC +//evowelsignbengali;09C7 +//evowelsigndeva;0947 +//evowelsigngujarati;0AC7 +//exclam;0021 +//exclamarmenian;055C +//exclamdbl;203C +//exclamdown;00A1 +//exclamdownsmall;F7A1 +//exclammonospace;FF01 +//exclamsmall;F721 +//existential;2203 +//ezh;0292 +//ezhcaron;01EF +//ezhcurl;0293 +//ezhreversed;01B9 +//ezhtail;01BA +//f;0066 +//fadeva;095E +//fagurmukhi;0A5E +//fahrenheit;2109 +//fathaarabic;064E +//fathalowarabic;064E +//fathatanarabic;064B +//fbopomofo;3108 +//fcircle;24D5 +//fdotaccent;1E1F +//feharabic;0641 +//feharmenian;0586 +//fehfinalarabic;FED2 +//fehinitialarabic;FED3 +//fehmedialarabic;FED4 +//feicoptic;03E5 +//female;2640 +//ff;FB00 +//ffi;FB03 +//ffl;FB04 +//fi;FB01 +//fifteencircle;246E +//fifteenparen;2482 +//fifteenperiod;2496 +//figuredash;2012 +//filledbox;25A0 +//filledrect;25AC +//finalkaf;05DA +//finalkafdagesh;FB3A +//finalkafdageshhebrew;FB3A +//finalkafhebrew;05DA +//finalkafqamats;05DA 05B8 +//finalkafqamatshebrew;05DA 05B8 +//finalkafsheva;05DA 05B0 +//finalkafshevahebrew;05DA 05B0 +//finalmem;05DD +//finalmemhebrew;05DD +//finalnun;05DF +//finalnunhebrew;05DF +//finalpe;05E3 +//finalpehebrew;05E3 +//finaltsadi;05E5 +//finaltsadihebrew;05E5 +//firsttonechinese;02C9 +//fisheye;25C9 +//fitacyrillic;0473 +//five;0035 +//fivearabic;0665 +//fivebengali;09EB +//fivecircle;2464 +//fivecircleinversesansserif;278E +//fivedeva;096B +//fiveeighths;215D +//fivegujarati;0AEB +//fivegurmukhi;0A6B +//fivehackarabic;0665 +//fivehangzhou;3025 +//fiveideographicparen;3224 +//fiveinferior;2085 +//fivemonospace;FF15 +//fiveoldstyle;F735 +//fiveparen;2478 +//fiveperiod;248C +//fivepersian;06F5 +//fiveroman;2174 +//fivesuperior;2075 +//fivethai;0E55 +//fl;FB02 +//florin;0192 +//fmonospace;FF46 +//fmsquare;3399 +//fofanthai;0E1F +//fofathai;0E1D +//fongmanthai;0E4F +//forall;2200 +//four;0034 +//fourarabic;0664 +//fourbengali;09EA +//fourcircle;2463 +//fourcircleinversesansserif;278D +//fourdeva;096A +//fourgujarati;0AEA +//fourgurmukhi;0A6A +//fourhackarabic;0664 +//fourhangzhou;3024 +//fourideographicparen;3223 +//fourinferior;2084 +//fourmonospace;FF14 +//fournumeratorbengali;09F7 +//fouroldstyle;F734 +//fourparen;2477 +//fourperiod;248B +//fourpersian;06F4 +//fourroman;2173 +//foursuperior;2074 +//fourteencircle;246D +//fourteenparen;2481 +//fourteenperiod;2495 +//fourthai;0E54 +//fourthtonechinese;02CB +//fparen;24A1 +//fraction;2044 +//franc;20A3 +//g;0067 +//gabengali;0997 +//gacute;01F5 +//gadeva;0917 +//gafarabic;06AF +//gaffinalarabic;FB93 +//gafinitialarabic;FB94 +//gafmedialarabic;FB95 +//gagujarati;0A97 +//gagurmukhi;0A17 +//gahiragana;304C +//gakatakana;30AC +//gamma;03B3 +//gammalatinsmall;0263 +//gammasuperior;02E0 +//gangiacoptic;03EB +//gbopomofo;310D +//gbreve;011F +//gcaron;01E7 +//gcedilla;0123 +//gcircle;24D6 +//gcircumflex;011D +//gcommaaccent;0123 +//gdot;0121 +//gdotaccent;0121 +//gecyrillic;0433 +//gehiragana;3052 +//gekatakana;30B2 +//geometricallyequal;2251 +//gereshaccenthebrew;059C +//gereshhebrew;05F3 +//gereshmuqdamhebrew;059D +//germandbls;00DF +//gershayimaccenthebrew;059E +//gershayimhebrew;05F4 +//getamark;3013 +//ghabengali;0998 +//ghadarmenian;0572 +//ghadeva;0918 +//ghagujarati;0A98 +//ghagurmukhi;0A18 +//ghainarabic;063A +//ghainfinalarabic;FECE +//ghaininitialarabic;FECF +//ghainmedialarabic;FED0 +//ghemiddlehookcyrillic;0495 +//ghestrokecyrillic;0493 +//gheupturncyrillic;0491 +//ghhadeva;095A +//ghhagurmukhi;0A5A +//ghook;0260 +//ghzsquare;3393 +//gihiragana;304E +//gikatakana;30AE +//gimarmenian;0563 +//gimel;05D2 +//gimeldagesh;FB32 +//gimeldageshhebrew;FB32 +//gimelhebrew;05D2 +//gjecyrillic;0453 +//glottalinvertedstroke;01BE +//glottalstop;0294 +//glottalstopinverted;0296 +//glottalstopmod;02C0 +//glottalstopreversed;0295 +//glottalstopreversedmod;02C1 +//glottalstopreversedsuperior;02E4 +//glottalstopstroke;02A1 +//glottalstopstrokereversed;02A2 +//gmacron;1E21 +//gmonospace;FF47 +//gohiragana;3054 +//gokatakana;30B4 +//gparen;24A2 +//gpasquare;33AC +//gradient;2207 +//grave;0060 +//gravebelowcmb;0316 +//gravecmb;0300 +//gravecomb;0300 +//gravedeva;0953 +//gravelowmod;02CE +//gravemonospace;FF40 +//gravetonecmb;0340 +//greater;003E +//greaterequal;2265 +//greaterequalorless;22DB +//greatermonospace;FF1E +//greaterorequivalent;2273 +//greaterorless;2277 +//greateroverequal;2267 +//greatersmall;FE65 +//gscript;0261 +//gstroke;01E5 +//guhiragana;3050 +//guillemotleft;00AB +//guillemotright;00BB +//guilsinglleft;2039 +//guilsinglright;203A +//gukatakana;30B0 +//guramusquare;3318 +//gysquare;33C9 +//h;0068 +//haabkhasiancyrillic;04A9 +//haaltonearabic;06C1 +//habengali;09B9 +//hadescendercyrillic;04B3 +//hadeva;0939 +//hagujarati;0AB9 +//hagurmukhi;0A39 +//haharabic;062D +//hahfinalarabic;FEA2 +//hahinitialarabic;FEA3 +//hahiragana;306F +//hahmedialarabic;FEA4 +//haitusquare;332A +//hakatakana;30CF +//hakatakanahalfwidth;FF8A +//halantgurmukhi;0A4D +//hamzaarabic;0621 +//hamzadammaarabic;0621 064F +//hamzadammatanarabic;0621 064C +//hamzafathaarabic;0621 064E +//hamzafathatanarabic;0621 064B +//hamzalowarabic;0621 +//hamzalowkasraarabic;0621 0650 +//hamzalowkasratanarabic;0621 064D +//hamzasukunarabic;0621 0652 +//hangulfiller;3164 +//hardsigncyrillic;044A +//harpoonleftbarbup;21BC +//harpoonrightbarbup;21C0 +//hasquare;33CA +//hatafpatah;05B2 +//hatafpatah16;05B2 +//hatafpatah23;05B2 +//hatafpatah2f;05B2 +//hatafpatahhebrew;05B2 +//hatafpatahnarrowhebrew;05B2 +//hatafpatahquarterhebrew;05B2 +//hatafpatahwidehebrew;05B2 +//hatafqamats;05B3 +//hatafqamats1b;05B3 +//hatafqamats28;05B3 +//hatafqamats34;05B3 +//hatafqamatshebrew;05B3 +//hatafqamatsnarrowhebrew;05B3 +//hatafqamatsquarterhebrew;05B3 +//hatafqamatswidehebrew;05B3 +//hatafsegol;05B1 +//hatafsegol17;05B1 +//hatafsegol24;05B1 +//hatafsegol30;05B1 +//hatafsegolhebrew;05B1 +//hatafsegolnarrowhebrew;05B1 +//hatafsegolquarterhebrew;05B1 +//hatafsegolwidehebrew;05B1 +//hbar;0127 +//hbopomofo;310F +//hbrevebelow;1E2B +//hcedilla;1E29 +//hcircle;24D7 +//hcircumflex;0125 +//hdieresis;1E27 +//hdotaccent;1E23 +//hdotbelow;1E25 +//he;05D4 +//heart;2665 +//heartsuitblack;2665 +//heartsuitwhite;2661 +//hedagesh;FB34 +//hedageshhebrew;FB34 +//hehaltonearabic;06C1 +//heharabic;0647 +//hehebrew;05D4 +//hehfinalaltonearabic;FBA7 +//hehfinalalttwoarabic;FEEA +//hehfinalarabic;FEEA +//hehhamzaabovefinalarabic;FBA5 +//hehhamzaaboveisolatedarabic;FBA4 +//hehinitialaltonearabic;FBA8 +//hehinitialarabic;FEEB +//hehiragana;3078 +//hehmedialaltonearabic;FBA9 +//hehmedialarabic;FEEC +//heiseierasquare;337B +//hekatakana;30D8 +//hekatakanahalfwidth;FF8D +//hekutaarusquare;3336 +//henghook;0267 +//herutusquare;3339 +//het;05D7 +//hethebrew;05D7 +//hhook;0266 +//hhooksuperior;02B1 +//hieuhacirclekorean;327B +//hieuhaparenkorean;321B +//hieuhcirclekorean;326D +//hieuhkorean;314E +//hieuhparenkorean;320D +//hihiragana;3072 +//hikatakana;30D2 +//hikatakanahalfwidth;FF8B +//hiriq;05B4 +//hiriq14;05B4 +//hiriq21;05B4 +//hiriq2d;05B4 +//hiriqhebrew;05B4 +//hiriqnarrowhebrew;05B4 +//hiriqquarterhebrew;05B4 +//hiriqwidehebrew;05B4 +//hlinebelow;1E96 +//hmonospace;FF48 +//hoarmenian;0570 +//hohipthai;0E2B +//hohiragana;307B +//hokatakana;30DB +//hokatakanahalfwidth;FF8E +//holam;05B9 +//holam19;05B9 +//holam26;05B9 +//holam32;05B9 +//holamhebrew;05B9 +//holamnarrowhebrew;05B9 +//holamquarterhebrew;05B9 +//holamwidehebrew;05B9 +//honokhukthai;0E2E +//hookabovecomb;0309 +//hookcmb;0309 +//hookpalatalizedbelowcmb;0321 +//hookretroflexbelowcmb;0322 +//hoonsquare;3342 +//horicoptic;03E9 +//horizontalbar;2015 +//horncmb;031B +//hotsprings;2668 +//house;2302 +//hparen;24A3 +//hsuperior;02B0 +//hturned;0265 +//huhiragana;3075 +//huiitosquare;3333 +//hukatakana;30D5 +//hukatakanahalfwidth;FF8C +//hungarumlaut;02DD +//hungarumlautcmb;030B +//hv;0195 +//hyphen;002D +//hypheninferior;F6E5 +//hyphenmonospace;FF0D +//hyphensmall;FE63 +//hyphensuperior;F6E6 +//hyphentwo;2010 +//i;0069 +//iacute;00ED +//iacyrillic;044F +//ibengali;0987 +//ibopomofo;3127 +//ibreve;012D +//icaron;01D0 +//icircle;24D8 +//icircumflex;00EE +//icyrillic;0456 +//idblgrave;0209 +//ideographearthcircle;328F +//ideographfirecircle;328B +//ideographicallianceparen;323F +//ideographiccallparen;323A +//ideographiccentrecircle;32A5 +//ideographicclose;3006 +//ideographiccomma;3001 +//ideographiccommaleft;FF64 +//ideographiccongratulationparen;3237 +//ideographiccorrectcircle;32A3 +//ideographicearthparen;322F +//ideographicenterpriseparen;323D +//ideographicexcellentcircle;329D +//ideographicfestivalparen;3240 +//ideographicfinancialcircle;3296 +//ideographicfinancialparen;3236 +//ideographicfireparen;322B +//ideographichaveparen;3232 +//ideographichighcircle;32A4 +//ideographiciterationmark;3005 +//ideographiclaborcircle;3298 +//ideographiclaborparen;3238 +//ideographicleftcircle;32A7 +//ideographiclowcircle;32A6 +//ideographicmedicinecircle;32A9 +//ideographicmetalparen;322E +//ideographicmoonparen;322A +//ideographicnameparen;3234 +//ideographicperiod;3002 +//ideographicprintcircle;329E +//ideographicreachparen;3243 +//ideographicrepresentparen;3239 +//ideographicresourceparen;323E +//ideographicrightcircle;32A8 +//ideographicsecretcircle;3299 +//ideographicselfparen;3242 +//ideographicsocietyparen;3233 +//ideographicspace;3000 +//ideographicspecialparen;3235 +//ideographicstockparen;3231 +//ideographicstudyparen;323B +//ideographicsunparen;3230 +//ideographicsuperviseparen;323C +//ideographicwaterparen;322C +//ideographicwoodparen;322D +//ideographiczero;3007 +//ideographmetalcircle;328E +//ideographmooncircle;328A +//ideographnamecircle;3294 +//ideographsuncircle;3290 +//ideographwatercircle;328C +//ideographwoodcircle;328D +//ideva;0907 +//idieresis;00EF +//idieresisacute;1E2F +//idieresiscyrillic;04E5 +//idotbelow;1ECB +//iebrevecyrillic;04D7 +//iecyrillic;0435 +//ieungacirclekorean;3275 +//ieungaparenkorean;3215 +//ieungcirclekorean;3267 +//ieungkorean;3147 +//ieungparenkorean;3207 +//igrave;00EC +//igujarati;0A87 +//igurmukhi;0A07 +//ihiragana;3044 +//ihookabove;1EC9 +//iibengali;0988 +//iicyrillic;0438 +//iideva;0908 +//iigujarati;0A88 +//iigurmukhi;0A08 +//iimatragurmukhi;0A40 +//iinvertedbreve;020B +//iishortcyrillic;0439 +//iivowelsignbengali;09C0 +//iivowelsigndeva;0940 +//iivowelsigngujarati;0AC0 +//ij;0133 +//ikatakana;30A4 +//ikatakanahalfwidth;FF72 +//ikorean;3163 +//ilde;02DC +//iluyhebrew;05AC +//imacron;012B +//imacroncyrillic;04E3 +//imageorapproximatelyequal;2253 +//imatragurmukhi;0A3F +//imonospace;FF49 +//increment;2206 +//infinity;221E +//iniarmenian;056B +//integral;222B +//integralbottom;2321 +//integralbt;2321 +//integralex;F8F5 +//integraltop;2320 +//integraltp;2320 +//intersection;2229 +//intisquare;3305 +//invbullet;25D8 +//invcircle;25D9 +//invsmileface;263B +//iocyrillic;0451 +//iogonek;012F +//iota;03B9 +//iotadieresis;03CA +//iotadieresistonos;0390 +//iotalatin;0269 +//iotatonos;03AF +//iparen;24A4 +//irigurmukhi;0A72 +//ismallhiragana;3043 +//ismallkatakana;30A3 +//ismallkatakanahalfwidth;FF68 +//issharbengali;09FA +//istroke;0268 +//isuperior;F6ED +//iterationhiragana;309D +//iterationkatakana;30FD +//itilde;0129 +//itildebelow;1E2D +//iubopomofo;3129 +//iucyrillic;044E +//ivowelsignbengali;09BF +//ivowelsigndeva;093F +//ivowelsigngujarati;0ABF +//izhitsacyrillic;0475 +//izhitsadblgravecyrillic;0477 +//j;006A +//jaarmenian;0571 +//jabengali;099C +//jadeva;091C +//jagujarati;0A9C +//jagurmukhi;0A1C +//jbopomofo;3110 +//jcaron;01F0 +//jcircle;24D9 +//jcircumflex;0135 +//jcrossedtail;029D +//jdotlessstroke;025F +//jecyrillic;0458 +//jeemarabic;062C +//jeemfinalarabic;FE9E +//jeeminitialarabic;FE9F +//jeemmedialarabic;FEA0 +//jeharabic;0698 +//jehfinalarabic;FB8B +//jhabengali;099D +//jhadeva;091D +//jhagujarati;0A9D +//jhagurmukhi;0A1D +//jheharmenian;057B +//jis;3004 +//jmonospace;FF4A +//jparen;24A5 +//jsuperior;02B2 +//k;006B +//kabashkircyrillic;04A1 +//kabengali;0995 +//kacute;1E31 +//kacyrillic;043A +//kadescendercyrillic;049B +//kadeva;0915 +//kaf;05DB +//kafarabic;0643 +//kafdagesh;FB3B +//kafdageshhebrew;FB3B +//kaffinalarabic;FEDA +//kafhebrew;05DB +//kafinitialarabic;FEDB +//kafmedialarabic;FEDC +//kafrafehebrew;FB4D +//kagujarati;0A95 +//kagurmukhi;0A15 +//kahiragana;304B +//kahookcyrillic;04C4 +//kakatakana;30AB +//kakatakanahalfwidth;FF76 +//kappa;03BA +//kappasymbolgreek;03F0 +//kapyeounmieumkorean;3171 +//kapyeounphieuphkorean;3184 +//kapyeounpieupkorean;3178 +//kapyeounssangpieupkorean;3179 +//karoriisquare;330D +//kashidaautoarabic;0640 +//kashidaautonosidebearingarabic;0640 +//kasmallkatakana;30F5 +//kasquare;3384 +//kasraarabic;0650 +//kasratanarabic;064D +//kastrokecyrillic;049F +//katahiraprolongmarkhalfwidth;FF70 +//kaverticalstrokecyrillic;049D +//kbopomofo;310E +//kcalsquare;3389 +//kcaron;01E9 +//kcedilla;0137 +//kcircle;24DA +//kcommaaccent;0137 +//kdotbelow;1E33 +//keharmenian;0584 +//kehiragana;3051 +//kekatakana;30B1 +//kekatakanahalfwidth;FF79 +//kenarmenian;056F +//kesmallkatakana;30F6 +//kgreenlandic;0138 +//khabengali;0996 +//khacyrillic;0445 +//khadeva;0916 +//khagujarati;0A96 +//khagurmukhi;0A16 +//khaharabic;062E +//khahfinalarabic;FEA6 +//khahinitialarabic;FEA7 +//khahmedialarabic;FEA8 +//kheicoptic;03E7 +//khhadeva;0959 +//khhagurmukhi;0A59 +//khieukhacirclekorean;3278 +//khieukhaparenkorean;3218 +//khieukhcirclekorean;326A +//khieukhkorean;314B +//khieukhparenkorean;320A +//khokhaithai;0E02 +//khokhonthai;0E05 +//khokhuatthai;0E03 +//khokhwaithai;0E04 +//khomutthai;0E5B +//khook;0199 +//khorakhangthai;0E06 +//khzsquare;3391 +//kihiragana;304D +//kikatakana;30AD +//kikatakanahalfwidth;FF77 +//kiroguramusquare;3315 +//kiromeetorusquare;3316 +//kirosquare;3314 +//kiyeokacirclekorean;326E +//kiyeokaparenkorean;320E +//kiyeokcirclekorean;3260 +//kiyeokkorean;3131 +//kiyeokparenkorean;3200 +//kiyeoksioskorean;3133 +//kjecyrillic;045C +//klinebelow;1E35 +//klsquare;3398 +//kmcubedsquare;33A6 +//kmonospace;FF4B +//kmsquaredsquare;33A2 +//kohiragana;3053 +//kohmsquare;33C0 +//kokaithai;0E01 +//kokatakana;30B3 +//kokatakanahalfwidth;FF7A +//kooposquare;331E +//koppacyrillic;0481 +//koreanstandardsymbol;327F +//koroniscmb;0343 +//kparen;24A6 +//kpasquare;33AA +//ksicyrillic;046F +//ktsquare;33CF +//kturned;029E +//kuhiragana;304F +//kukatakana;30AF +//kukatakanahalfwidth;FF78 +//kvsquare;33B8 +//kwsquare;33BE +//l;006C +//labengali;09B2 +//lacute;013A +//ladeva;0932 +//lagujarati;0AB2 +//lagurmukhi;0A32 +//lakkhangyaothai;0E45 +//lamaleffinalarabic;FEFC +//lamalefhamzaabovefinalarabic;FEF8 +//lamalefhamzaaboveisolatedarabic;FEF7 +//lamalefhamzabelowfinalarabic;FEFA +//lamalefhamzabelowisolatedarabic;FEF9 +//lamalefisolatedarabic;FEFB +//lamalefmaddaabovefinalarabic;FEF6 +//lamalefmaddaaboveisolatedarabic;FEF5 +//lamarabic;0644 +//lambda;03BB +//lambdastroke;019B +//lamed;05DC +//lameddagesh;FB3C +//lameddageshhebrew;FB3C +//lamedhebrew;05DC +//lamedholam;05DC 05B9 +//lamedholamdagesh;05DC 05B9 05BC +//lamedholamdageshhebrew;05DC 05B9 05BC +//lamedholamhebrew;05DC 05B9 +//lamfinalarabic;FEDE +//lamhahinitialarabic;FCCA +//laminitialarabic;FEDF +//lamjeeminitialarabic;FCC9 +//lamkhahinitialarabic;FCCB +//lamlamhehisolatedarabic;FDF2 +//lammedialarabic;FEE0 +//lammeemhahinitialarabic;FD88 +//lammeeminitialarabic;FCCC +//lammeemjeeminitialarabic;FEDF FEE4 FEA0 +//lammeemkhahinitialarabic;FEDF FEE4 FEA8 +//largecircle;25EF +//lbar;019A +//lbelt;026C +//lbopomofo;310C +//lcaron;013E +//lcedilla;013C +//lcircle;24DB +//lcircumflexbelow;1E3D +//lcommaaccent;013C +//ldot;0140 +//ldotaccent;0140 +//ldotbelow;1E37 +//ldotbelowmacron;1E39 +//leftangleabovecmb;031A +//lefttackbelowcmb;0318 +//less;003C +//lessequal;2264 +//lessequalorgreater;22DA +//lessmonospace;FF1C +//lessorequivalent;2272 +//lessorgreater;2276 +//lessoverequal;2266 +//lesssmall;FE64 +//lezh;026E +//lfblock;258C +//lhookretroflex;026D +//lira;20A4 +//liwnarmenian;056C +//lj;01C9 +//ljecyrillic;0459 +//ll;F6C0 +//lladeva;0933 +//llagujarati;0AB3 +//llinebelow;1E3B +//llladeva;0934 +//llvocalicbengali;09E1 +//llvocalicdeva;0961 +//llvocalicvowelsignbengali;09E3 +//llvocalicvowelsigndeva;0963 +//lmiddletilde;026B +//lmonospace;FF4C +//lmsquare;33D0 +//lochulathai;0E2C +//logicaland;2227 +//logicalnot;00AC +//logicalnotreversed;2310 +//logicalor;2228 +//lolingthai;0E25 +//longs;017F +//lowlinecenterline;FE4E +//lowlinecmb;0332 +//lowlinedashed;FE4D +//lozenge;25CA +//lparen;24A7 +//lslash;0142 +//lsquare;2113 +//lsuperior;F6EE +//ltshade;2591 +//luthai;0E26 +//lvocalicbengali;098C +//lvocalicdeva;090C +//lvocalicvowelsignbengali;09E2 +//lvocalicvowelsigndeva;0962 +//lxsquare;33D3 +//m;006D +//mabengali;09AE +//macron;00AF +//macronbelowcmb;0331 +//macroncmb;0304 +//macronlowmod;02CD +//macronmonospace;FFE3 +//macute;1E3F +//madeva;092E +//magujarati;0AAE +//magurmukhi;0A2E +//mahapakhhebrew;05A4 +//mahapakhlefthebrew;05A4 +//mahiragana;307E +//maichattawalowleftthai;F895 +//maichattawalowrightthai;F894 +//maichattawathai;0E4B +//maichattawaupperleftthai;F893 +//maieklowleftthai;F88C +//maieklowrightthai;F88B +//maiekthai;0E48 +//maiekupperleftthai;F88A +//maihanakatleftthai;F884 +//maihanakatthai;0E31 +//maitaikhuleftthai;F889 +//maitaikhuthai;0E47 +//maitholowleftthai;F88F +//maitholowrightthai;F88E +//maithothai;0E49 +//maithoupperleftthai;F88D +//maitrilowleftthai;F892 +//maitrilowrightthai;F891 +//maitrithai;0E4A +//maitriupperleftthai;F890 +//maiyamokthai;0E46 +//makatakana;30DE +//makatakanahalfwidth;FF8F +//male;2642 +//mansyonsquare;3347 +//maqafhebrew;05BE +//mars;2642 +//masoracirclehebrew;05AF +//masquare;3383 +//mbopomofo;3107 +//mbsquare;33D4 +//mcircle;24DC +//mcubedsquare;33A5 +//mdotaccent;1E41 +//mdotbelow;1E43 +//meemarabic;0645 +//meemfinalarabic;FEE2 +//meeminitialarabic;FEE3 +//meemmedialarabic;FEE4 +//meemmeeminitialarabic;FCD1 +//meemmeemisolatedarabic;FC48 +//meetorusquare;334D +//mehiragana;3081 +//meizierasquare;337E +//mekatakana;30E1 +//mekatakanahalfwidth;FF92 +//mem;05DE +//memdagesh;FB3E +//memdageshhebrew;FB3E +//memhebrew;05DE +//menarmenian;0574 +//merkhahebrew;05A5 +//merkhakefulahebrew;05A6 +//merkhakefulalefthebrew;05A6 +//merkhalefthebrew;05A5 +//mhook;0271 +//mhzsquare;3392 +//middledotkatakanahalfwidth;FF65 +//middot;00B7 +//mieumacirclekorean;3272 +//mieumaparenkorean;3212 +//mieumcirclekorean;3264 +//mieumkorean;3141 +//mieumpansioskorean;3170 +//mieumparenkorean;3204 +//mieumpieupkorean;316E +//mieumsioskorean;316F +//mihiragana;307F +//mikatakana;30DF +//mikatakanahalfwidth;FF90 +//minus;2212 +//minusbelowcmb;0320 +//minuscircle;2296 +//minusmod;02D7 +//minusplus;2213 +//minute;2032 +//miribaarusquare;334A +//mirisquare;3349 +//mlonglegturned;0270 +//mlsquare;3396 +//mmcubedsquare;33A3 +//mmonospace;FF4D +//mmsquaredsquare;339F +//mohiragana;3082 +//mohmsquare;33C1 +//mokatakana;30E2 +//mokatakanahalfwidth;FF93 +//molsquare;33D6 +//momathai;0E21 +//moverssquare;33A7 +//moverssquaredsquare;33A8 +//mparen;24A8 +//mpasquare;33AB +//mssquare;33B3 +//msuperior;F6EF +//mturned;026F +//mu;00B5 +//mu1;00B5 +//muasquare;3382 +//muchgreater;226B +//muchless;226A +//mufsquare;338C +//mugreek;03BC +//mugsquare;338D +//muhiragana;3080 +//mukatakana;30E0 +//mukatakanahalfwidth;FF91 +//mulsquare;3395 +//multiply;00D7 +//mumsquare;339B +//munahhebrew;05A3 +//munahlefthebrew;05A3 +//musicalnote;266A +//musicalnotedbl;266B +//musicflatsign;266D +//musicsharpsign;266F +//mussquare;33B2 +//muvsquare;33B6 +//muwsquare;33BC +//mvmegasquare;33B9 +//mvsquare;33B7 +//mwmegasquare;33BF +//mwsquare;33BD +//n;006E +//nabengali;09A8 +//nabla;2207 +//nacute;0144 +//nadeva;0928 +//nagujarati;0AA8 +//nagurmukhi;0A28 +//nahiragana;306A +//nakatakana;30CA +//nakatakanahalfwidth;FF85 +//napostrophe;0149 +//nasquare;3381 +//nbopomofo;310B +//nbspace;00A0 +//ncaron;0148 +//ncedilla;0146 +//ncircle;24DD +//ncircumflexbelow;1E4B +//ncommaaccent;0146 +//ndotaccent;1E45 +//ndotbelow;1E47 +//nehiragana;306D +//nekatakana;30CD +//nekatakanahalfwidth;FF88 +//newsheqelsign;20AA +//nfsquare;338B +//ngabengali;0999 +//ngadeva;0919 +//ngagujarati;0A99 +//ngagurmukhi;0A19 +//ngonguthai;0E07 +//nhiragana;3093 +//nhookleft;0272 +//nhookretroflex;0273 +//nieunacirclekorean;326F +//nieunaparenkorean;320F +//nieuncieuckorean;3135 +//nieuncirclekorean;3261 +//nieunhieuhkorean;3136 +//nieunkorean;3134 +//nieunpansioskorean;3168 +//nieunparenkorean;3201 +//nieunsioskorean;3167 +//nieuntikeutkorean;3166 +//nihiragana;306B +//nikatakana;30CB +//nikatakanahalfwidth;FF86 +//nikhahitleftthai;F899 +//nikhahitthai;0E4D +//nine;0039 +//ninearabic;0669 +//ninebengali;09EF +//ninecircle;2468 +//ninecircleinversesansserif;2792 +//ninedeva;096F +//ninegujarati;0AEF +//ninegurmukhi;0A6F +//ninehackarabic;0669 +//ninehangzhou;3029 +//nineideographicparen;3228 +//nineinferior;2089 +//ninemonospace;FF19 +//nineoldstyle;F739 +//nineparen;247C +//nineperiod;2490 +//ninepersian;06F9 +//nineroman;2178 +//ninesuperior;2079 +//nineteencircle;2472 +//nineteenparen;2486 +//nineteenperiod;249A +//ninethai;0E59 +//nj;01CC +//njecyrillic;045A +//nkatakana;30F3 +//nkatakanahalfwidth;FF9D +//nlegrightlong;019E +//nlinebelow;1E49 +//nmonospace;FF4E +//nmsquare;339A +//nnabengali;09A3 +//nnadeva;0923 +//nnagujarati;0AA3 +//nnagurmukhi;0A23 +//nnnadeva;0929 +//nohiragana;306E +//nokatakana;30CE +//nokatakanahalfwidth;FF89 +//nonbreakingspace;00A0 +//nonenthai;0E13 +//nonuthai;0E19 +//noonarabic;0646 +//noonfinalarabic;FEE6 +//noonghunnaarabic;06BA +//noonghunnafinalarabic;FB9F +//noonhehinitialarabic;FEE7 FEEC +//nooninitialarabic;FEE7 +//noonjeeminitialarabic;FCD2 +//noonjeemisolatedarabic;FC4B +//noonmedialarabic;FEE8 +//noonmeeminitialarabic;FCD5 +//noonmeemisolatedarabic;FC4E +//noonnoonfinalarabic;FC8D +//notcontains;220C +//notelement;2209 +//notelementof;2209 +//notequal;2260 +//notgreater;226F +//notgreaternorequal;2271 +//notgreaternorless;2279 +//notidentical;2262 +//notless;226E +//notlessnorequal;2270 +//notparallel;2226 +//notprecedes;2280 +//notsubset;2284 +//notsucceeds;2281 +//notsuperset;2285 +//nowarmenian;0576 +//nparen;24A9 +//nssquare;33B1 +//nsuperior;207F +//ntilde;00F1 +//nu;03BD +//nuhiragana;306C +//nukatakana;30CC +//nukatakanahalfwidth;FF87 +//nuktabengali;09BC +//nuktadeva;093C +//nuktagujarati;0ABC +//nuktagurmukhi;0A3C +//numbersign;0023 +//numbersignmonospace;FF03 +//numbersignsmall;FE5F +//numeralsigngreek;0374 +//numeralsignlowergreek;0375 +//numero;2116 +//nun;05E0 +//nundagesh;FB40 +//nundageshhebrew;FB40 +//nunhebrew;05E0 +//nvsquare;33B5 +//nwsquare;33BB +//nyabengali;099E +//nyadeva;091E +//nyagujarati;0A9E +//nyagurmukhi;0A1E +//o;006F +//oacute;00F3 +//oangthai;0E2D +//obarred;0275 +//obarredcyrillic;04E9 +//obarreddieresiscyrillic;04EB +//obengali;0993 +//obopomofo;311B +//obreve;014F +//ocandradeva;0911 +//ocandragujarati;0A91 +//ocandravowelsigndeva;0949 +//ocandravowelsigngujarati;0AC9 +//ocaron;01D2 +//ocircle;24DE +//ocircumflex;00F4 +//ocircumflexacute;1ED1 +//ocircumflexdotbelow;1ED9 +//ocircumflexgrave;1ED3 +//ocircumflexhookabove;1ED5 +//ocircumflextilde;1ED7 +//ocyrillic;043E +//odblacute;0151 +//odblgrave;020D +//odeva;0913 +//odieresis;00F6 +//odieresiscyrillic;04E7 +//odotbelow;1ECD +//oe;0153 +//oekorean;315A +//ogonek;02DB +//ogonekcmb;0328 +//ograve;00F2 +//ogujarati;0A93 +//oharmenian;0585 +//ohiragana;304A +//ohookabove;1ECF +//ohorn;01A1 +//ohornacute;1EDB +//ohorndotbelow;1EE3 +//ohorngrave;1EDD +//ohornhookabove;1EDF +//ohorntilde;1EE1 +//ohungarumlaut;0151 +//oi;01A3 +//oinvertedbreve;020F +//okatakana;30AA +//okatakanahalfwidth;FF75 +//okorean;3157 +//olehebrew;05AB +//omacron;014D +//omacronacute;1E53 +//omacrongrave;1E51 +//omdeva;0950 +//omega;03C9 +//omega1;03D6 +//omegacyrillic;0461 +//omegalatinclosed;0277 +//omegaroundcyrillic;047B +//omegatitlocyrillic;047D +//omegatonos;03CE +//omgujarati;0AD0 +//omicron;03BF +//omicrontonos;03CC +//omonospace;FF4F +//one;0031 +//onearabic;0661 +//onebengali;09E7 +//onecircle;2460 +//onecircleinversesansserif;278A +//onedeva;0967 +//onedotenleader;2024 +//oneeighth;215B +//onefitted;F6DC +//onegujarati;0AE7 +//onegurmukhi;0A67 +//onehackarabic;0661 +//onehalf;00BD +//onehangzhou;3021 +//oneideographicparen;3220 +//oneinferior;2081 +//onemonospace;FF11 +//onenumeratorbengali;09F4 +//oneoldstyle;F731 +//oneparen;2474 +//oneperiod;2488 +//onepersian;06F1 +//onequarter;00BC +//oneroman;2170 +//onesuperior;00B9 +//onethai;0E51 +//onethird;2153 +//oogonek;01EB +//oogonekmacron;01ED +//oogurmukhi;0A13 +//oomatragurmukhi;0A4B +//oopen;0254 +//oparen;24AA +//openbullet;25E6 +//option;2325 +//ordfeminine;00AA +//ordmasculine;00BA +//orthogonal;221F +//oshortdeva;0912 +//oshortvowelsigndeva;094A +//oslash;00F8 +//oslashacute;01FF +//osmallhiragana;3049 +//osmallkatakana;30A9 +//osmallkatakanahalfwidth;FF6B +//ostrokeacute;01FF +//osuperior;F6F0 +//otcyrillic;047F +//otilde;00F5 +//otildeacute;1E4D +//otildedieresis;1E4F +//oubopomofo;3121 +//overline;203E +//overlinecenterline;FE4A +//overlinecmb;0305 +//overlinedashed;FE49 +//overlinedblwavy;FE4C +//overlinewavy;FE4B +//overscore;00AF +//ovowelsignbengali;09CB +//ovowelsigndeva;094B +//ovowelsigngujarati;0ACB +//p;0070 +//paampssquare;3380 +//paasentosquare;332B +//pabengali;09AA +//pacute;1E55 +//padeva;092A +//pagedown;21DF +//pageup;21DE +//pagujarati;0AAA +//pagurmukhi;0A2A +//pahiragana;3071 +//paiyannoithai;0E2F +//pakatakana;30D1 +//palatalizationcyrilliccmb;0484 +//palochkacyrillic;04C0 +//pansioskorean;317F +//paragraph;00B6 +//parallel;2225 +//parenleft;0028 +//parenleftaltonearabic;FD3E +//parenleftbt;F8ED +//parenleftex;F8EC +//parenleftinferior;208D +//parenleftmonospace;FF08 +//parenleftsmall;FE59 +//parenleftsuperior;207D +//parenlefttp;F8EB +//parenleftvertical;FE35 +//parenright;0029 +//parenrightaltonearabic;FD3F +//parenrightbt;F8F8 +//parenrightex;F8F7 +//parenrightinferior;208E +//parenrightmonospace;FF09 +//parenrightsmall;FE5A +//parenrightsuperior;207E +//parenrighttp;F8F6 +//parenrightvertical;FE36 +//partialdiff;2202 +//paseqhebrew;05C0 +//pashtahebrew;0599 +//pasquare;33A9 +//patah;05B7 +//patah11;05B7 +//patah1d;05B7 +//patah2a;05B7 +//patahhebrew;05B7 +//patahnarrowhebrew;05B7 +//patahquarterhebrew;05B7 +//patahwidehebrew;05B7 +//pazerhebrew;05A1 +//pbopomofo;3106 +//pcircle;24DF +//pdotaccent;1E57 +//pe;05E4 +//pecyrillic;043F +//pedagesh;FB44 +//pedageshhebrew;FB44 +//peezisquare;333B +//pefinaldageshhebrew;FB43 +//peharabic;067E +//peharmenian;057A +//pehebrew;05E4 +//pehfinalarabic;FB57 +//pehinitialarabic;FB58 +//pehiragana;307A +//pehmedialarabic;FB59 +//pekatakana;30DA +//pemiddlehookcyrillic;04A7 +//perafehebrew;FB4E +//percent;0025 +//percentarabic;066A +//percentmonospace;FF05 +//percentsmall;FE6A +//period;002E +//periodarmenian;0589 +//periodcentered;00B7 +//periodhalfwidth;FF61 +//periodinferior;F6E7 +//periodmonospace;FF0E +//periodsmall;FE52 +//periodsuperior;F6E8 +//perispomenigreekcmb;0342 +//perpendicular;22A5 +//perthousand;2030 +//peseta;20A7 +//pfsquare;338A +//phabengali;09AB +//phadeva;092B +//phagujarati;0AAB +//phagurmukhi;0A2B +//phi;03C6 +//phi1;03D5 +//phieuphacirclekorean;327A +//phieuphaparenkorean;321A +//phieuphcirclekorean;326C +//phieuphkorean;314D +//phieuphparenkorean;320C +//philatin;0278 +//phinthuthai;0E3A +//phisymbolgreek;03D5 +//phook;01A5 +//phophanthai;0E1E +//phophungthai;0E1C +//phosamphaothai;0E20 +//pi;03C0 +//pieupacirclekorean;3273 +//pieupaparenkorean;3213 +//pieupcieuckorean;3176 +//pieupcirclekorean;3265 +//pieupkiyeokkorean;3172 +//pieupkorean;3142 +//pieupparenkorean;3205 +//pieupsioskiyeokkorean;3174 +//pieupsioskorean;3144 +//pieupsiostikeutkorean;3175 +//pieupthieuthkorean;3177 +//pieuptikeutkorean;3173 +//pihiragana;3074 +//pikatakana;30D4 +//pisymbolgreek;03D6 +//piwrarmenian;0583 +//plus;002B +//plusbelowcmb;031F +//pluscircle;2295 +//plusminus;00B1 +//plusmod;02D6 +//plusmonospace;FF0B +//plussmall;FE62 +//plussuperior;207A +//pmonospace;FF50 +//pmsquare;33D8 +//pohiragana;307D +//pointingindexdownwhite;261F +//pointingindexleftwhite;261C +//pointingindexrightwhite;261E +//pointingindexupwhite;261D +//pokatakana;30DD +//poplathai;0E1B +//postalmark;3012 +//postalmarkface;3020 +//pparen;24AB +//precedes;227A +//prescription;211E +//primemod;02B9 +//primereversed;2035 +//product;220F +//projective;2305 +//prolongedkana;30FC +//propellor;2318 +//propersubset;2282 +//propersuperset;2283 +//proportion;2237 +//proportional;221D +//psi;03C8 +//psicyrillic;0471 +//psilipneumatacyrilliccmb;0486 +//pssquare;33B0 +//puhiragana;3077 +//pukatakana;30D7 +//pvsquare;33B4 +//pwsquare;33BA +//q;0071 +//qadeva;0958 +//qadmahebrew;05A8 +//qafarabic;0642 +//qaffinalarabic;FED6 +//qafinitialarabic;FED7 +//qafmedialarabic;FED8 +//qamats;05B8 +//qamats10;05B8 +//qamats1a;05B8 +//qamats1c;05B8 +//qamats27;05B8 +//qamats29;05B8 +//qamats33;05B8 +//qamatsde;05B8 +//qamatshebrew;05B8 +//qamatsnarrowhebrew;05B8 +//qamatsqatanhebrew;05B8 +//qamatsqatannarrowhebrew;05B8 +//qamatsqatanquarterhebrew;05B8 +//qamatsqatanwidehebrew;05B8 +//qamatsquarterhebrew;05B8 +//qamatswidehebrew;05B8 +//qarneyparahebrew;059F +//qbopomofo;3111 +//qcircle;24E0 +//qhook;02A0 +//qmonospace;FF51 +//qof;05E7 +//qofdagesh;FB47 +//qofdageshhebrew;FB47 +//qofhatafpatah;05E7 05B2 +//qofhatafpatahhebrew;05E7 05B2 +//qofhatafsegol;05E7 05B1 +//qofhatafsegolhebrew;05E7 05B1 +//qofhebrew;05E7 +//qofhiriq;05E7 05B4 +//qofhiriqhebrew;05E7 05B4 +//qofholam;05E7 05B9 +//qofholamhebrew;05E7 05B9 +//qofpatah;05E7 05B7 +//qofpatahhebrew;05E7 05B7 +//qofqamats;05E7 05B8 +//qofqamatshebrew;05E7 05B8 +//qofqubuts;05E7 05BB +//qofqubutshebrew;05E7 05BB +//qofsegol;05E7 05B6 +//qofsegolhebrew;05E7 05B6 +//qofsheva;05E7 05B0 +//qofshevahebrew;05E7 05B0 +//qoftsere;05E7 05B5 +//qoftserehebrew;05E7 05B5 +//qparen;24AC +//quarternote;2669 +//qubuts;05BB +//qubuts18;05BB +//qubuts25;05BB +//qubuts31;05BB +//qubutshebrew;05BB +//qubutsnarrowhebrew;05BB +//qubutsquarterhebrew;05BB +//qubutswidehebrew;05BB +//question;003F +//questionarabic;061F +//questionarmenian;055E +//questiondown;00BF +//questiondownsmall;F7BF +//questiongreek;037E +//questionmonospace;FF1F +//questionsmall;F73F +//quotedbl;0022 +//quotedblbase;201E +//quotedblleft;201C +//quotedblmonospace;FF02 +//quotedblprime;301E +//quotedblprimereversed;301D +//quotedblright;201D +//quoteleft;2018 +//quoteleftreversed;201B +//quotereversed;201B +//quoteright;2019 +//quoterightn;0149 +//quotesinglbase;201A +//quotesingle;0027 +//quotesinglemonospace;FF07 +//r;0072 +//raarmenian;057C +//rabengali;09B0 +//racute;0155 +//radeva;0930 +//radical;221A +//radicalex;F8E5 +//radoverssquare;33AE +//radoverssquaredsquare;33AF +//radsquare;33AD +//rafe;05BF +//rafehebrew;05BF +//ragujarati;0AB0 +//ragurmukhi;0A30 +//rahiragana;3089 +//rakatakana;30E9 +//rakatakanahalfwidth;FF97 +//ralowerdiagonalbengali;09F1 +//ramiddlediagonalbengali;09F0 +//ramshorn;0264 +//ratio;2236 +//rbopomofo;3116 +//rcaron;0159 +//rcedilla;0157 +//rcircle;24E1 +//rcommaaccent;0157 +//rdblgrave;0211 +//rdotaccent;1E59 +//rdotbelow;1E5B +//rdotbelowmacron;1E5D +//referencemark;203B +//reflexsubset;2286 +//reflexsuperset;2287 +//registered;00AE +//registersans;F8E8 +//registerserif;F6DA +//reharabic;0631 +//reharmenian;0580 +//rehfinalarabic;FEAE +//rehiragana;308C +//rehyehaleflamarabic;0631 FEF3 FE8E 0644 +//rekatakana;30EC +//rekatakanahalfwidth;FF9A +//resh;05E8 +//reshdageshhebrew;FB48 +//reshhatafpatah;05E8 05B2 +//reshhatafpatahhebrew;05E8 05B2 +//reshhatafsegol;05E8 05B1 +//reshhatafsegolhebrew;05E8 05B1 +//reshhebrew;05E8 +//reshhiriq;05E8 05B4 +//reshhiriqhebrew;05E8 05B4 +//reshholam;05E8 05B9 +//reshholamhebrew;05E8 05B9 +//reshpatah;05E8 05B7 +//reshpatahhebrew;05E8 05B7 +//reshqamats;05E8 05B8 +//reshqamatshebrew;05E8 05B8 +//reshqubuts;05E8 05BB +//reshqubutshebrew;05E8 05BB +//reshsegol;05E8 05B6 +//reshsegolhebrew;05E8 05B6 +//reshsheva;05E8 05B0 +//reshshevahebrew;05E8 05B0 +//reshtsere;05E8 05B5 +//reshtserehebrew;05E8 05B5 +//reversedtilde;223D +//reviahebrew;0597 +//reviamugrashhebrew;0597 +//revlogicalnot;2310 +//rfishhook;027E +//rfishhookreversed;027F +//rhabengali;09DD +//rhadeva;095D +//rho;03C1 +//rhook;027D +//rhookturned;027B +//rhookturnedsuperior;02B5 +//rhosymbolgreek;03F1 +//rhotichookmod;02DE +//rieulacirclekorean;3271 +//rieulaparenkorean;3211 +//rieulcirclekorean;3263 +//rieulhieuhkorean;3140 +//rieulkiyeokkorean;313A +//rieulkiyeoksioskorean;3169 +//rieulkorean;3139 +//rieulmieumkorean;313B +//rieulpansioskorean;316C +//rieulparenkorean;3203 +//rieulphieuphkorean;313F +//rieulpieupkorean;313C +//rieulpieupsioskorean;316B +//rieulsioskorean;313D +//rieulthieuthkorean;313E +//rieultikeutkorean;316A +//rieulyeorinhieuhkorean;316D +//rightangle;221F +//righttackbelowcmb;0319 +//righttriangle;22BF +//rihiragana;308A +//rikatakana;30EA +//rikatakanahalfwidth;FF98 +//ring;02DA +//ringbelowcmb;0325 +//ringcmb;030A +//ringhalfleft;02BF +//ringhalfleftarmenian;0559 +//ringhalfleftbelowcmb;031C +//ringhalfleftcentered;02D3 +//ringhalfright;02BE +//ringhalfrightbelowcmb;0339 +//ringhalfrightcentered;02D2 +//rinvertedbreve;0213 +//rittorusquare;3351 +//rlinebelow;1E5F +//rlongleg;027C +//rlonglegturned;027A +//rmonospace;FF52 +//rohiragana;308D +//rokatakana;30ED +//rokatakanahalfwidth;FF9B +//roruathai;0E23 +//rparen;24AD +//rrabengali;09DC +//rradeva;0931 +//rragurmukhi;0A5C +//rreharabic;0691 +//rrehfinalarabic;FB8D +//rrvocalicbengali;09E0 +//rrvocalicdeva;0960 +//rrvocalicgujarati;0AE0 +//rrvocalicvowelsignbengali;09C4 +//rrvocalicvowelsigndeva;0944 +//rrvocalicvowelsigngujarati;0AC4 +//rsuperior;F6F1 +//rtblock;2590 +//rturned;0279 +//rturnedsuperior;02B4 +//ruhiragana;308B +//rukatakana;30EB +//rukatakanahalfwidth;FF99 +//rupeemarkbengali;09F2 +//rupeesignbengali;09F3 +//rupiah;F6DD +//ruthai;0E24 +//rvocalicbengali;098B +//rvocalicdeva;090B +//rvocalicgujarati;0A8B +//rvocalicvowelsignbengali;09C3 +//rvocalicvowelsigndeva;0943 +//rvocalicvowelsigngujarati;0AC3 +//s;0073 +//sabengali;09B8 +//sacute;015B +//sacutedotaccent;1E65 +//sadarabic;0635 +//sadeva;0938 +//sadfinalarabic;FEBA +//sadinitialarabic;FEBB +//sadmedialarabic;FEBC +//sagujarati;0AB8 +//sagurmukhi;0A38 +//sahiragana;3055 +//sakatakana;30B5 +//sakatakanahalfwidth;FF7B +//sallallahoualayhewasallamarabic;FDFA +//samekh;05E1 +//samekhdagesh;FB41 +//samekhdageshhebrew;FB41 +//samekhhebrew;05E1 +//saraaathai;0E32 +//saraaethai;0E41 +//saraaimaimalaithai;0E44 +//saraaimaimuanthai;0E43 +//saraamthai;0E33 +//saraathai;0E30 +//saraethai;0E40 +//saraiileftthai;F886 +//saraiithai;0E35 +//saraileftthai;F885 +//saraithai;0E34 +//saraothai;0E42 +//saraueeleftthai;F888 +//saraueethai;0E37 +//saraueleftthai;F887 +//sarauethai;0E36 +//sarauthai;0E38 +//sarauuthai;0E39 +//sbopomofo;3119 +//scaron;0161 +//scarondotaccent;1E67 +//scedilla;015F +//schwa;0259 +//schwacyrillic;04D9 +//schwadieresiscyrillic;04DB +//schwahook;025A +//scircle;24E2 +//scircumflex;015D +//scommaaccent;0219 +//sdotaccent;1E61 +//sdotbelow;1E63 +//sdotbelowdotaccent;1E69 +//seagullbelowcmb;033C +//second;2033 +//secondtonechinese;02CA +//section;00A7 +//seenarabic;0633 +//seenfinalarabic;FEB2 +//seeninitialarabic;FEB3 +//seenmedialarabic;FEB4 +//segol;05B6 +//segol13;05B6 +//segol1f;05B6 +//segol2c;05B6 +//segolhebrew;05B6 +//segolnarrowhebrew;05B6 +//segolquarterhebrew;05B6 +//segoltahebrew;0592 +//segolwidehebrew;05B6 +//seharmenian;057D +//sehiragana;305B +//sekatakana;30BB +//sekatakanahalfwidth;FF7E +//semicolon;003B +//semicolonarabic;061B +//semicolonmonospace;FF1B +//semicolonsmall;FE54 +//semivoicedmarkkana;309C +//semivoicedmarkkanahalfwidth;FF9F +//sentisquare;3322 +//sentosquare;3323 +//seven;0037 +//sevenarabic;0667 +//sevenbengali;09ED +//sevencircle;2466 +//sevencircleinversesansserif;2790 +//sevendeva;096D +//seveneighths;215E +//sevengujarati;0AED +//sevengurmukhi;0A6D +//sevenhackarabic;0667 +//sevenhangzhou;3027 +//sevenideographicparen;3226 +//seveninferior;2087 +//sevenmonospace;FF17 +//sevenoldstyle;F737 +//sevenparen;247A +//sevenperiod;248E +//sevenpersian;06F7 +//sevenroman;2176 +//sevensuperior;2077 +//seventeencircle;2470 +//seventeenparen;2484 +//seventeenperiod;2498 +//seventhai;0E57 +//sfthyphen;00AD +//shaarmenian;0577 +//shabengali;09B6 +//shacyrillic;0448 +//shaddaarabic;0651 +//shaddadammaarabic;FC61 +//shaddadammatanarabic;FC5E +//shaddafathaarabic;FC60 +//shaddafathatanarabic;0651 064B +//shaddakasraarabic;FC62 +//shaddakasratanarabic;FC5F +//shade;2592 +//shadedark;2593 +//shadelight;2591 +//shademedium;2592 +//shadeva;0936 +//shagujarati;0AB6 +//shagurmukhi;0A36 +//shalshelethebrew;0593 +//shbopomofo;3115 +//shchacyrillic;0449 +//sheenarabic;0634 +//sheenfinalarabic;FEB6 +//sheeninitialarabic;FEB7 +//sheenmedialarabic;FEB8 +//sheicoptic;03E3 +//sheqel;20AA +//sheqelhebrew;20AA +//sheva;05B0 +//sheva115;05B0 +//sheva15;05B0 +//sheva22;05B0 +//sheva2e;05B0 +//shevahebrew;05B0 +//shevanarrowhebrew;05B0 +//shevaquarterhebrew;05B0 +//shevawidehebrew;05B0 +//shhacyrillic;04BB +//shimacoptic;03ED +//shin;05E9 +//shindagesh;FB49 +//shindageshhebrew;FB49 +//shindageshshindot;FB2C +//shindageshshindothebrew;FB2C +//shindageshsindot;FB2D +//shindageshsindothebrew;FB2D +//shindothebrew;05C1 +//shinhebrew;05E9 +//shinshindot;FB2A +//shinshindothebrew;FB2A +//shinsindot;FB2B +//shinsindothebrew;FB2B +//shook;0282 +//sigma;03C3 +//sigma1;03C2 +//sigmafinal;03C2 +//sigmalunatesymbolgreek;03F2 +//sihiragana;3057 +//sikatakana;30B7 +//sikatakanahalfwidth;FF7C +//siluqhebrew;05BD +//siluqlefthebrew;05BD +//similar;223C +//sindothebrew;05C2 +//siosacirclekorean;3274 +//siosaparenkorean;3214 +//sioscieuckorean;317E +//sioscirclekorean;3266 +//sioskiyeokkorean;317A +//sioskorean;3145 +//siosnieunkorean;317B +//siosparenkorean;3206 +//siospieupkorean;317D +//siostikeutkorean;317C +//six;0036 +//sixarabic;0666 +//sixbengali;09EC +//sixcircle;2465 +//sixcircleinversesansserif;278F +//sixdeva;096C +//sixgujarati;0AEC +//sixgurmukhi;0A6C +//sixhackarabic;0666 +//sixhangzhou;3026 +//sixideographicparen;3225 +//sixinferior;2086 +//sixmonospace;FF16 +//sixoldstyle;F736 +//sixparen;2479 +//sixperiod;248D +//sixpersian;06F6 +//sixroman;2175 +//sixsuperior;2076 +//sixteencircle;246F +//sixteencurrencydenominatorbengali;09F9 +//sixteenparen;2483 +//sixteenperiod;2497 +//sixthai;0E56 +//slash;002F +//slashmonospace;FF0F +//slong;017F +//slongdotaccent;1E9B +//smileface;263A +//smonospace;FF53 +//sofpasuqhebrew;05C3 +//softhyphen;00AD +//softsigncyrillic;044C +//sohiragana;305D +//sokatakana;30BD +//sokatakanahalfwidth;FF7F +//soliduslongoverlaycmb;0338 +//solidusshortoverlaycmb;0337 +//sorusithai;0E29 +//sosalathai;0E28 +//sosothai;0E0B +//sosuathai;0E2A +//space;0020 +//spacehackarabic;0020 +//spade;2660 +//spadesuitblack;2660 +//spadesuitwhite;2664 +//sparen;24AE +//squarebelowcmb;033B +//squarecc;33C4 +//squarecm;339D +//squarediagonalcrosshatchfill;25A9 +//squarehorizontalfill;25A4 +//squarekg;338F +//squarekm;339E +//squarekmcapital;33CE +//squareln;33D1 +//squarelog;33D2 +//squaremg;338E +//squaremil;33D5 +//squaremm;339C +//squaremsquared;33A1 +//squareorthogonalcrosshatchfill;25A6 +//squareupperlefttolowerrightfill;25A7 +//squareupperrighttolowerleftfill;25A8 +//squareverticalfill;25A5 +//squarewhitewithsmallblack;25A3 +//srsquare;33DB +//ssabengali;09B7 +//ssadeva;0937 +//ssagujarati;0AB7 +//ssangcieuckorean;3149 +//ssanghieuhkorean;3185 +//ssangieungkorean;3180 +//ssangkiyeokkorean;3132 +//ssangnieunkorean;3165 +//ssangpieupkorean;3143 +//ssangsioskorean;3146 +//ssangtikeutkorean;3138 +//ssuperior;F6F2 +//sterling;00A3 +//sterlingmonospace;FFE1 +//strokelongoverlaycmb;0336 +//strokeshortoverlaycmb;0335 +//subset;2282 +//subsetnotequal;228A +//subsetorequal;2286 +//succeeds;227B +//suchthat;220B +//suhiragana;3059 +//sukatakana;30B9 +//sukatakanahalfwidth;FF7D +//sukunarabic;0652 +//summation;2211 +//sun;263C +//superset;2283 +//supersetnotequal;228B +//supersetorequal;2287 +//svsquare;33DC +//syouwaerasquare;337C +//t;0074 +//tabengali;09A4 +//tackdown;22A4 +//tackleft;22A3 +//tadeva;0924 +//tagujarati;0AA4 +//tagurmukhi;0A24 +//taharabic;0637 +//tahfinalarabic;FEC2 +//tahinitialarabic;FEC3 +//tahiragana;305F +//tahmedialarabic;FEC4 +//taisyouerasquare;337D +//takatakana;30BF +//takatakanahalfwidth;FF80 +//tatweelarabic;0640 +//tau;03C4 +//tav;05EA +//tavdages;FB4A +//tavdagesh;FB4A +//tavdageshhebrew;FB4A +//tavhebrew;05EA +//tbar;0167 +//tbopomofo;310A +//tcaron;0165 +//tccurl;02A8 +//tcedilla;0163 +//tcheharabic;0686 +//tchehfinalarabic;FB7B +//tchehinitialarabic;FB7C +//tchehmedialarabic;FB7D +//tchehmeeminitialarabic;FB7C FEE4 +//tcircle;24E3 +//tcircumflexbelow;1E71 +//tcommaaccent;0163 +//tdieresis;1E97 +//tdotaccent;1E6B +//tdotbelow;1E6D +//tecyrillic;0442 +//tedescendercyrillic;04AD +//teharabic;062A +//tehfinalarabic;FE96 +//tehhahinitialarabic;FCA2 +//tehhahisolatedarabic;FC0C +//tehinitialarabic;FE97 +//tehiragana;3066 +//tehjeeminitialarabic;FCA1 +//tehjeemisolatedarabic;FC0B +//tehmarbutaarabic;0629 +//tehmarbutafinalarabic;FE94 +//tehmedialarabic;FE98 +//tehmeeminitialarabic;FCA4 +//tehmeemisolatedarabic;FC0E +//tehnoonfinalarabic;FC73 +//tekatakana;30C6 +//tekatakanahalfwidth;FF83 +//telephone;2121 +//telephoneblack;260E +//telishagedolahebrew;05A0 +//telishaqetanahebrew;05A9 +//tencircle;2469 +//tenideographicparen;3229 +//tenparen;247D +//tenperiod;2491 +//tenroman;2179 +//tesh;02A7 +//tet;05D8 +//tetdagesh;FB38 +//tetdageshhebrew;FB38 +//tethebrew;05D8 +//tetsecyrillic;04B5 +//tevirhebrew;059B +//tevirlefthebrew;059B +//thabengali;09A5 +//thadeva;0925 +//thagujarati;0AA5 +//thagurmukhi;0A25 +//thalarabic;0630 +//thalfinalarabic;FEAC +//thanthakhatlowleftthai;F898 +//thanthakhatlowrightthai;F897 +//thanthakhatthai;0E4C +//thanthakhatupperleftthai;F896 +//theharabic;062B +//thehfinalarabic;FE9A +//thehinitialarabic;FE9B +//thehmedialarabic;FE9C +//thereexists;2203 +//therefore;2234 +//theta;03B8 +//theta1;03D1 +//thetasymbolgreek;03D1 +//thieuthacirclekorean;3279 +//thieuthaparenkorean;3219 +//thieuthcirclekorean;326B +//thieuthkorean;314C +//thieuthparenkorean;320B +//thirteencircle;246C +//thirteenparen;2480 +//thirteenperiod;2494 +//thonangmonthothai;0E11 +//thook;01AD +//thophuthaothai;0E12 +//thorn;00FE +//thothahanthai;0E17 +//thothanthai;0E10 +//thothongthai;0E18 +//thothungthai;0E16 +//thousandcyrillic;0482 +//thousandsseparatorarabic;066C +//thousandsseparatorpersian;066C +//three;0033 +//threearabic;0663 +//threebengali;09E9 +//threecircle;2462 +//threecircleinversesansserif;278C +//threedeva;0969 +//threeeighths;215C +//threegujarati;0AE9 +//threegurmukhi;0A69 +//threehackarabic;0663 +//threehangzhou;3023 +//threeideographicparen;3222 +//threeinferior;2083 +//threemonospace;FF13 +//threenumeratorbengali;09F6 +//threeoldstyle;F733 +//threeparen;2476 +//threeperiod;248A +//threepersian;06F3 +//threequarters;00BE +//threequartersemdash;F6DE +//threeroman;2172 +//threesuperior;00B3 +//threethai;0E53 +//thzsquare;3394 +//tihiragana;3061 +//tikatakana;30C1 +//tikatakanahalfwidth;FF81 +//tikeutacirclekorean;3270 +//tikeutaparenkorean;3210 +//tikeutcirclekorean;3262 +//tikeutkorean;3137 +//tikeutparenkorean;3202 +//tilde;02DC +//tildebelowcmb;0330 +//tildecmb;0303 +//tildecomb;0303 +//tildedoublecmb;0360 +//tildeoperator;223C +//tildeoverlaycmb;0334 +//tildeverticalcmb;033E +//timescircle;2297 +//tipehahebrew;0596 +//tipehalefthebrew;0596 +//tippigurmukhi;0A70 +//titlocyrilliccmb;0483 +//tiwnarmenian;057F +//tlinebelow;1E6F +//tmonospace;FF54 +//toarmenian;0569 +//tohiragana;3068 +//tokatakana;30C8 +//tokatakanahalfwidth;FF84 +//tonebarextrahighmod;02E5 +//tonebarextralowmod;02E9 +//tonebarhighmod;02E6 +//tonebarlowmod;02E8 +//tonebarmidmod;02E7 +//tonefive;01BD +//tonesix;0185 +//tonetwo;01A8 +//tonos;0384 +//tonsquare;3327 +//topatakthai;0E0F +//tortoiseshellbracketleft;3014 +//tortoiseshellbracketleftsmall;FE5D +//tortoiseshellbracketleftvertical;FE39 +//tortoiseshellbracketright;3015 +//tortoiseshellbracketrightsmall;FE5E +//tortoiseshellbracketrightvertical;FE3A +//totaothai;0E15 +//tpalatalhook;01AB +//tparen;24AF +//trademark;2122 +//trademarksans;F8EA +//trademarkserif;F6DB +//tretroflexhook;0288 +//triagdn;25BC +//triaglf;25C4 +//triagrt;25BA +//triagup;25B2 +//ts;02A6 +//tsadi;05E6 +//tsadidagesh;FB46 +//tsadidageshhebrew;FB46 +//tsadihebrew;05E6 +//tsecyrillic;0446 +//tsere;05B5 +//tsere12;05B5 +//tsere1e;05B5 +//tsere2b;05B5 +//tserehebrew;05B5 +//tserenarrowhebrew;05B5 +//tserequarterhebrew;05B5 +//tserewidehebrew;05B5 +//tshecyrillic;045B +//tsuperior;F6F3 +//ttabengali;099F +//ttadeva;091F +//ttagujarati;0A9F +//ttagurmukhi;0A1F +//tteharabic;0679 +//ttehfinalarabic;FB67 +//ttehinitialarabic;FB68 +//ttehmedialarabic;FB69 +//tthabengali;09A0 +//tthadeva;0920 +//tthagujarati;0AA0 +//tthagurmukhi;0A20 +//tturned;0287 +//tuhiragana;3064 +//tukatakana;30C4 +//tukatakanahalfwidth;FF82 +//tusmallhiragana;3063 +//tusmallkatakana;30C3 +//tusmallkatakanahalfwidth;FF6F +//twelvecircle;246B +//twelveparen;247F +//twelveperiod;2493 +//twelveroman;217B +//twentycircle;2473 +//twentyhangzhou;5344 +//twentyparen;2487 +//twentyperiod;249B +//two;0032 +//twoarabic;0662 +//twobengali;09E8 +//twocircle;2461 +//twocircleinversesansserif;278B +//twodeva;0968 +//twodotenleader;2025 +//twodotleader;2025 +//twodotleadervertical;FE30 +//twogujarati;0AE8 +//twogurmukhi;0A68 +//twohackarabic;0662 +//twohangzhou;3022 +//twoideographicparen;3221 +//twoinferior;2082 +//twomonospace;FF12 +//twonumeratorbengali;09F5 +//twooldstyle;F732 +//twoparen;2475 +//twoperiod;2489 +//twopersian;06F2 +//tworoman;2171 +//twostroke;01BB +//twosuperior;00B2 +//twothai;0E52 +//twothirds;2154 +//u;0075 +//uacute;00FA +//ubar;0289 +//ubengali;0989 +//ubopomofo;3128 +//ubreve;016D +//ucaron;01D4 +//ucircle;24E4 +//ucircumflex;00FB +//ucircumflexbelow;1E77 +//ucyrillic;0443 +//udattadeva;0951 +//udblacute;0171 +//udblgrave;0215 +//udeva;0909 +//udieresis;00FC +//udieresisacute;01D8 +//udieresisbelow;1E73 +//udieresiscaron;01DA +//udieresiscyrillic;04F1 +//udieresisgrave;01DC +//udieresismacron;01D6 +//udotbelow;1EE5 +//ugrave;00F9 +//ugujarati;0A89 +//ugurmukhi;0A09 +//uhiragana;3046 +//uhookabove;1EE7 +//uhorn;01B0 +//uhornacute;1EE9 +//uhorndotbelow;1EF1 +//uhorngrave;1EEB +//uhornhookabove;1EED +//uhorntilde;1EEF +//uhungarumlaut;0171 +//uhungarumlautcyrillic;04F3 +//uinvertedbreve;0217 +//ukatakana;30A6 +//ukatakanahalfwidth;FF73 +//ukcyrillic;0479 +//ukorean;315C +//umacron;016B +//umacroncyrillic;04EF +//umacrondieresis;1E7B +//umatragurmukhi;0A41 +//umonospace;FF55 +//underscore;005F +//underscoredbl;2017 +//underscoremonospace;FF3F +//underscorevertical;FE33 +//underscorewavy;FE4F +//union;222A +//universal;2200 +//uogonek;0173 +//uparen;24B0 +//upblock;2580 +//upperdothebrew;05C4 +//upsilon;03C5 +//upsilondieresis;03CB +//upsilondieresistonos;03B0 +//upsilonlatin;028A +//upsilontonos;03CD +//uptackbelowcmb;031D +//uptackmod;02D4 +//uragurmukhi;0A73 +//uring;016F +//ushortcyrillic;045E +//usmallhiragana;3045 +//usmallkatakana;30A5 +//usmallkatakanahalfwidth;FF69 +//ustraightcyrillic;04AF +//ustraightstrokecyrillic;04B1 +//utilde;0169 +//utildeacute;1E79 +//utildebelow;1E75 +//uubengali;098A +//uudeva;090A +//uugujarati;0A8A +//uugurmukhi;0A0A +//uumatragurmukhi;0A42 +//uuvowelsignbengali;09C2 +//uuvowelsigndeva;0942 +//uuvowelsigngujarati;0AC2 +//uvowelsignbengali;09C1 +//uvowelsigndeva;0941 +//uvowelsigngujarati;0AC1 +//v;0076 +//vadeva;0935 +//vagujarati;0AB5 +//vagurmukhi;0A35 +//vakatakana;30F7 +//vav;05D5 +//vavdagesh;FB35 +//vavdagesh65;FB35 +//vavdageshhebrew;FB35 +//vavhebrew;05D5 +//vavholam;FB4B +//vavholamhebrew;FB4B +//vavvavhebrew;05F0 +//vavyodhebrew;05F1 +//vcircle;24E5 +//vdotbelow;1E7F +//vecyrillic;0432 +//veharabic;06A4 +//vehfinalarabic;FB6B +//vehinitialarabic;FB6C +//vehmedialarabic;FB6D +//vekatakana;30F9 +//venus;2640 +//verticalbar;007C +//verticallineabovecmb;030D +//verticallinebelowcmb;0329 +//verticallinelowmod;02CC +//verticallinemod;02C8 +//vewarmenian;057E +//vhook;028B +//vikatakana;30F8 +//viramabengali;09CD +//viramadeva;094D +//viramagujarati;0ACD +//visargabengali;0983 +//visargadeva;0903 +//visargagujarati;0A83 +//vmonospace;FF56 +//voarmenian;0578 +//voicediterationhiragana;309E +//voicediterationkatakana;30FE +//voicedmarkkana;309B +//voicedmarkkanahalfwidth;FF9E +//vokatakana;30FA +//vparen;24B1 +//vtilde;1E7D +//vturned;028C +//vuhiragana;3094 +//vukatakana;30F4 +//w;0077 +//wacute;1E83 +//waekorean;3159 +//wahiragana;308F +//wakatakana;30EF +//wakatakanahalfwidth;FF9C +//wakorean;3158 +//wasmallhiragana;308E +//wasmallkatakana;30EE +//wattosquare;3357 +//wavedash;301C +//wavyunderscorevertical;FE34 +//wawarabic;0648 +//wawfinalarabic;FEEE +//wawhamzaabovearabic;0624 +//wawhamzaabovefinalarabic;FE86 +//wbsquare;33DD +//wcircle;24E6 +//wcircumflex;0175 +//wdieresis;1E85 +//wdotaccent;1E87 +//wdotbelow;1E89 +//wehiragana;3091 +//weierstrass;2118 +//wekatakana;30F1 +//wekorean;315E +//weokorean;315D +//wgrave;1E81 +//whitebullet;25E6 +//whitecircle;25CB +//whitecircleinverse;25D9 +//whitecornerbracketleft;300E +//whitecornerbracketleftvertical;FE43 +//whitecornerbracketright;300F +//whitecornerbracketrightvertical;FE44 +//whitediamond;25C7 +//whitediamondcontainingblacksmalldiamond;25C8 +//whitedownpointingsmalltriangle;25BF +//whitedownpointingtriangle;25BD +//whiteleftpointingsmalltriangle;25C3 +//whiteleftpointingtriangle;25C1 +//whitelenticularbracketleft;3016 +//whitelenticularbracketright;3017 +//whiterightpointingsmalltriangle;25B9 +//whiterightpointingtriangle;25B7 +//whitesmallsquare;25AB +//whitesmilingface;263A +//whitesquare;25A1 +//whitestar;2606 +//whitetelephone;260F +//whitetortoiseshellbracketleft;3018 +//whitetortoiseshellbracketright;3019 +//whiteuppointingsmalltriangle;25B5 +//whiteuppointingtriangle;25B3 +//wihiragana;3090 +//wikatakana;30F0 +//wikorean;315F +//wmonospace;FF57 +//wohiragana;3092 +//wokatakana;30F2 +//wokatakanahalfwidth;FF66 +//won;20A9 +//wonmonospace;FFE6 +//wowaenthai;0E27 +//wparen;24B2 +//wring;1E98 +//wsuperior;02B7 +//wturned;028D +//wynn;01BF +//x;0078 +//xabovecmb;033D +//xbopomofo;3112 +//xcircle;24E7 +//xdieresis;1E8D +//xdotaccent;1E8B +//xeharmenian;056D +//xi;03BE +//xmonospace;FF58 +//xparen;24B3 +//xsuperior;02E3 +//y;0079 +//yaadosquare;334E +//yabengali;09AF +//yacute;00FD +//yadeva;092F +//yaekorean;3152 +//yagujarati;0AAF +//yagurmukhi;0A2F +//yahiragana;3084 +//yakatakana;30E4 +//yakatakanahalfwidth;FF94 +//yakorean;3151 +//yamakkanthai;0E4E +//yasmallhiragana;3083 +//yasmallkatakana;30E3 +//yasmallkatakanahalfwidth;FF6C +//yatcyrillic;0463 +//ycircle;24E8 +//ycircumflex;0177 +//ydieresis;00FF +//ydotaccent;1E8F +//ydotbelow;1EF5 +//yeharabic;064A +//yehbarreearabic;06D2 +//yehbarreefinalarabic;FBAF +//yehfinalarabic;FEF2 +//yehhamzaabovearabic;0626 +//yehhamzaabovefinalarabic;FE8A +//yehhamzaaboveinitialarabic;FE8B +//yehhamzaabovemedialarabic;FE8C +//yehinitialarabic;FEF3 +//yehmedialarabic;FEF4 +//yehmeeminitialarabic;FCDD +//yehmeemisolatedarabic;FC58 +//yehnoonfinalarabic;FC94 +//yehthreedotsbelowarabic;06D1 +//yekorean;3156 +//yen;00A5 +//yenmonospace;FFE5 +//yeokorean;3155 +//yeorinhieuhkorean;3186 +//yerahbenyomohebrew;05AA +//yerahbenyomolefthebrew;05AA +//yericyrillic;044B +//yerudieresiscyrillic;04F9 +//yesieungkorean;3181 +//yesieungpansioskorean;3183 +//yesieungsioskorean;3182 +//yetivhebrew;059A +//ygrave;1EF3 +//yhook;01B4 +//yhookabove;1EF7 +//yiarmenian;0575 +//yicyrillic;0457 +//yikorean;3162 +//yinyang;262F +//yiwnarmenian;0582 +//ymonospace;FF59 +//yod;05D9 +//yoddagesh;FB39 +//yoddageshhebrew;FB39 +//yodhebrew;05D9 +//yodyodhebrew;05F2 +//yodyodpatahhebrew;FB1F +//yohiragana;3088 +//yoikorean;3189 +//yokatakana;30E8 +//yokatakanahalfwidth;FF96 +//yokorean;315B +//yosmallhiragana;3087 +//yosmallkatakana;30E7 +//yosmallkatakanahalfwidth;FF6E +//yotgreek;03F3 +//yoyaekorean;3188 +//yoyakorean;3187 +//yoyakthai;0E22 +//yoyingthai;0E0D +//yparen;24B4 +//ypogegrammeni;037A +//ypogegrammenigreekcmb;0345 +//yr;01A6 +//yring;1E99 +//ysuperior;02B8 +//ytilde;1EF9 +//yturned;028E +//yuhiragana;3086 +//yuikorean;318C +//yukatakana;30E6 +//yukatakanahalfwidth;FF95 +//yukorean;3160 +//yusbigcyrillic;046B +//yusbigiotifiedcyrillic;046D +//yuslittlecyrillic;0467 +//yuslittleiotifiedcyrillic;0469 +//yusmallhiragana;3085 +//yusmallkatakana;30E5 +//yusmallkatakanahalfwidth;FF6D +//yuyekorean;318B +//yuyeokorean;318A +//yyabengali;09DF +//yyadeva;095F +//z;007A +//zaarmenian;0566 +//zacute;017A +//zadeva;095B +//zagurmukhi;0A5B +//zaharabic;0638 +//zahfinalarabic;FEC6 +//zahinitialarabic;FEC7 +//zahiragana;3056 +//zahmedialarabic;FEC8 +//zainarabic;0632 +//zainfinalarabic;FEB0 +//zakatakana;30B6 +//zaqefgadolhebrew;0595 +//zaqefqatanhebrew;0594 +//zarqahebrew;0598 +//zayin;05D6 +//zayindagesh;FB36 +//zayindageshhebrew;FB36 +//zayinhebrew;05D6 +//zbopomofo;3117 +//zcaron;017E +//zcircle;24E9 +//zcircumflex;1E91 +//zcurl;0291 +//zdot;017C +//zdotaccent;017C +//zdotbelow;1E93 +//zecyrillic;0437 +//zedescendercyrillic;0499 +//zedieresiscyrillic;04DF +//zehiragana;305C +//zekatakana;30BC +//zero;0030 +//zeroarabic;0660 +//zerobengali;09E6 +//zerodeva;0966 +//zerogujarati;0AE6 +//zerogurmukhi;0A66 +//zerohackarabic;0660 +//zeroinferior;2080 +//zeromonospace;FF10 +//zerooldstyle;F730 +//zeropersian;06F0 +//zerosuperior;2070 +//zerothai;0E50 +//zerowidthjoiner;FEFF +//zerowidthnonjoiner;200C +//zerowidthspace;200B +//zeta;03B6 +//zhbopomofo;3113 +//zhearmenian;056A +//zhebrevecyrillic;04C2 +//zhecyrillic;0436 +//zhedescendercyrillic;0497 +//zhedieresiscyrillic;04DD +//zihiragana;3058 +//zikatakana;30B8 +//zinorhebrew;05AE +//zlinebelow;1E95 +//zmonospace;FF5A +//zohiragana;305E +//zokatakana;30BE +//zparen;24B5 +//zretroflexhook;0290 +//zstroke;01B6 +//zuhiragana;305A +//zukatakana;30BA + + } +} +#endif \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphListForNewFonts.cs b/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphListForNewFonts.cs new file mode 100644 index 00000000..6a695858 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/AdobeGlyphListForNewFonts.cs @@ -0,0 +1,4223 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Fonts +{ +#if true_ +#if !SILVERLIGHT + /// + /// Testing only + /// + public sealed class AdobeGlyphListForNewFonts + { + AdobeGlyphListForNewFonts() { } + + // ReSharper disable InconsistentNaming + + /// + /// LATIN CAPITAL LETTER A + /// + public const char A = '\u0041'; + + /// + /// LATIN CAPITAL LETTER AE + /// + public const char AE = '\u00C6'; + + /// + /// LATIN CAPITAL LETTER AE WITH ACUTE + /// + public const char AEacute = '\u01FC'; + + /// + /// LATIN CAPITAL LETTER A WITH ACUTE + /// + public const char Aacute = '\u00C1'; + + /// + /// LATIN CAPITAL LETTER A WITH BREVE + /// + public const char Abreve = '\u0102'; + + /// + /// LATIN CAPITAL LETTER A WITH CIRCUMFLEX + /// + public const char Acircumflex = '\u00C2'; + + /// + /// LATIN CAPITAL LETTER A WITH DIAERESIS + /// + public const char Adieresis = '\u00C4'; + + /// + /// LATIN CAPITAL LETTER A WITH GRAVE + /// + public const char Agrave = '\u00C0'; + + /// + /// GREEK CAPITAL LETTER ALPHA + /// + public const char Alpha = '\u0391'; + + /// + /// GREEK CAPITAL LETTER ALPHA WITH TONOS + /// + public const char Alphatonos = '\u0386'; + + /// + /// LATIN CAPITAL LETTER A WITH MACRON + /// + public const char Amacron = '\u0100'; + + /// + /// LATIN CAPITAL LETTER A WITH OGONEK + /// + public const char Aogonek = '\u0104'; + + /// + /// LATIN CAPITAL LETTER A WITH RING ABOVE + /// + public const char Aring = '\u00C5'; + + /// + /// LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE + /// + public const char Aringacute = '\u01FA'; + + /// + /// LATIN CAPITAL LETTER A WITH TILDE + /// + public const char Atilde = '\u00C3'; + + /// + /// LATIN CAPITAL LETTER B + /// + public const char B = '\u0042'; + + /// + /// GREEK CAPITAL LETTER BETA + /// + public const char Beta = '\u0392'; + + /// + /// LATIN CAPITAL LETTER C + /// + public const char C = '\u0043'; + + /// + /// LATIN CAPITAL LETTER C WITH ACUTE + /// + public const char Cacute = '\u0106'; + + /// + /// LATIN CAPITAL LETTER C WITH CARON + /// + public const char Ccaron = '\u010C'; + + /// + /// LATIN CAPITAL LETTER C WITH CEDILLA + /// + public const char Ccedilla = '\u00C7'; + + /// + /// LATIN CAPITAL LETTER C WITH CIRCUMFLEX + /// + public const char Ccircumflex = '\u0108'; + + /// + /// LATIN CAPITAL LETTER C WITH DOT ABOVE + /// + public const char Cdotaccent = '\u010A'; + + /// + /// GREEK CAPITAL LETTER CHI + /// + public const char Chi = '\u03A7'; + + /// + /// LATIN CAPITAL LETTER D + /// + public const char D = '\u0044'; + + /// + /// LATIN CAPITAL LETTER D WITH CARON + /// + public const char Dcaron = '\u010E'; + + /// + /// LATIN CAPITAL LETTER D WITH STROKE + /// + public const char Dcroat = '\u0110'; + + /// + /// INCREMENT + /// + public const char Delta = '\u2206'; + + /// + /// LATIN CAPITAL LETTER E + /// + public const char E = '\u0045'; + + /// + /// LATIN CAPITAL LETTER E WITH ACUTE + /// + public const char Eacute = '\u00C9'; + + /// + /// LATIN CAPITAL LETTER E WITH BREVE + /// + public const char Ebreve = '\u0114'; + + /// + /// LATIN CAPITAL LETTER E WITH CARON + /// + public const char Ecaron = '\u011A'; + + /// + /// LATIN CAPITAL LETTER E WITH CIRCUMFLEX + /// + public const char Ecircumflex = '\u00CA'; + + /// + /// LATIN CAPITAL LETTER E WITH DIAERESIS + /// + public const char Edieresis = '\u00CB'; + + /// + /// LATIN CAPITAL LETTER E WITH DOT ABOVE + /// + public const char Edotaccent = '\u0116'; + + /// + /// LATIN CAPITAL LETTER E WITH GRAVE + /// + public const char Egrave = '\u00C8'; + + /// + /// LATIN CAPITAL LETTER E WITH MACRON + /// + public const char Emacron = '\u0112'; + + /// + /// LATIN CAPITAL LETTER ENG + /// + public const char Eng = '\u014A'; + + /// + /// LATIN CAPITAL LETTER E WITH OGONEK + /// + public const char Eogonek = '\u0118'; + + /// + /// GREEK CAPITAL LETTER EPSILON + /// + public const char Epsilon = '\u0395'; + + /// + /// GREEK CAPITAL LETTER EPSILON WITH TONOS + /// + public const char Epsilontonos = '\u0388'; + + /// + /// GREEK CAPITAL LETTER ETA + /// + public const char Eta = '\u0397'; + + /// + /// GREEK CAPITAL LETTER ETA WITH TONOS + /// + public const char Etatonos = '\u0389'; + + /// + /// LATIN CAPITAL LETTER ETH + /// + public const char Eth = '\u00D0'; + + /// + /// EURO SIGN + /// + public const char Euro = '\u20AC'; + + /// + /// LATIN CAPITAL LETTER F + /// + public const char F = '\u0046'; + + /// + /// LATIN CAPITAL LETTER G + /// + public const char G = '\u0047'; + + /// + /// GREEK CAPITAL LETTER GAMMA + /// + public const char Gamma = '\u0393'; + + /// + /// LATIN CAPITAL LETTER G WITH BREVE + /// + public const char Gbreve = '\u011E'; + + /// + /// LATIN CAPITAL LETTER G WITH CARON + /// + public const char Gcaron = '\u01E6'; + + /// + /// LATIN CAPITAL LETTER G WITH CIRCUMFLEX + /// + public const char Gcircumflex = '\u011C'; + + /// + /// LATIN CAPITAL LETTER G WITH CEDILLA + /// + public const char Gcommaaccent = '\u0122'; + + /// + /// LATIN CAPITAL LETTER G WITH DOT ABOVE + /// + public const char Gdotaccent = '\u0120'; + + /// + /// LATIN CAPITAL LETTER H + /// + public const char H = '\u0048'; + + /// + /// BLACK CIRCLE + /// + public const char H18533 = '\u25CF'; + + /// + /// BLACK SMALL SQUARE + /// + public const char H18543 = '\u25AA'; + + /// + /// WHITE SMALL SQUARE + /// + public const char H18551 = '\u25AB'; + + /// + /// WHITE SQUARE + /// + public const char H22073 = '\u25A1'; + + /// + /// LATIN CAPITAL LETTER H WITH STROKE + /// + public const char Hbar = '\u0126'; + + /// + /// LATIN CAPITAL LETTER H WITH CIRCUMFLEX + /// + public const char Hcircumflex = '\u0124'; + + /// + /// LATIN CAPITAL LETTER I + /// + public const char I = '\u0049'; + + /// + /// LATIN CAPITAL LIGATURE IJ + /// + public const char IJ = '\u0132'; + + /// + /// LATIN CAPITAL LETTER I WITH ACUTE + /// + public const char Iacute = '\u00CD'; + + /// + /// LATIN CAPITAL LETTER I WITH BREVE + /// + public const char Ibreve = '\u012C'; + + /// + /// LATIN CAPITAL LETTER I WITH CIRCUMFLEX + /// + public const char Icircumflex = '\u00CE'; + + /// + /// LATIN CAPITAL LETTER I WITH DIAERESIS + /// + public const char Idieresis = '\u00CF'; + + /// + /// LATIN CAPITAL LETTER I WITH DOT ABOVE + /// + public const char Idotaccent = '\u0130'; + + /// + /// BLACK-LETTER CAPITAL I + /// + public const char Ifraktur = '\u2111'; + + /// + /// LATIN CAPITAL LETTER I WITH GRAVE + /// + public const char Igrave = '\u00CC'; + + /// + /// LATIN CAPITAL LETTER I WITH MACRON + /// + public const char Imacron = '\u012A'; + + /// + /// LATIN CAPITAL LETTER I WITH OGONEK + /// + public const char Iogonek = '\u012E'; + + /// + /// GREEK CAPITAL LETTER IOTA + /// + public const char Iota = '\u0399'; + + /// + /// GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + /// + public const char Iotadieresis = '\u03AA'; + + /// + /// GREEK CAPITAL LETTER IOTA WITH TONOS + /// + public const char Iotatonos = '\u038A'; + + /// + /// LATIN CAPITAL LETTER I WITH TILDE + /// + public const char Itilde = '\u0128'; + + /// + /// LATIN CAPITAL LETTER J + /// + public const char J = '\u004A'; + + /// + /// LATIN CAPITAL LETTER J WITH CIRCUMFLEX + /// + public const char Jcircumflex = '\u0134'; + + /// + /// LATIN CAPITAL LETTER K + /// + public const char K = '\u004B'; + + /// + /// GREEK CAPITAL LETTER KAPPA + /// + public const char Kappa = '\u039A'; + + /// + /// LATIN CAPITAL LETTER K WITH CEDILLA + /// + public const char Kcommaaccent = '\u0136'; + + /// + /// LATIN CAPITAL LETTER L + /// + public const char L = '\u004C'; + + /// + /// LATIN CAPITAL LETTER L WITH ACUTE + /// + public const char Lacute = '\u0139'; + + /// + /// GREEK CAPITAL LETTER LAMDA + /// + public const char Lambda = '\u039B'; + + /// + /// LATIN CAPITAL LETTER L WITH CARON + /// + public const char Lcaron = '\u013D'; + + /// + /// LATIN CAPITAL LETTER L WITH CEDILLA + /// + public const char Lcommaaccent = '\u013B'; + + /// + /// LATIN CAPITAL LETTER L WITH MIDDLE DOT + /// + public const char Ldot = '\u013F'; + + /// + /// LATIN CAPITAL LETTER L WITH STROKE + /// + public const char Lslash = '\u0141'; + + /// + /// LATIN CAPITAL LETTER M + /// + public const char M = '\u004D'; + + /// + /// GREEK CAPITAL LETTER MU + /// + public const char Mu = '\u039C'; + + /// + /// LATIN CAPITAL LETTER N + /// + public const char N = '\u004E'; + + /// + /// LATIN CAPITAL LETTER N WITH ACUTE + /// + public const char Nacute = '\u0143'; + + /// + /// LATIN CAPITAL LETTER N WITH CARON + /// + public const char Ncaron = '\u0147'; + + /// + /// LATIN CAPITAL LETTER N WITH CEDILLA + /// + public const char Ncommaaccent = '\u0145'; + + /// + /// LATIN CAPITAL LETTER N WITH TILDE + /// + public const char Ntilde = '\u00D1'; + + /// + /// GREEK CAPITAL LETTER NU + /// + public const char Nu = '\u039D'; + + /// + /// LATIN CAPITAL LETTER O + /// + public const char O = '\u004F'; + + /// + /// LATIN CAPITAL LIGATURE OE + /// + public const char OE = '\u0152'; + + /// + /// LATIN CAPITAL LETTER O WITH ACUTE + /// + public const char Oacute = '\u00D3'; + + /// + /// LATIN CAPITAL LETTER O WITH BREVE + /// + public const char Obreve = '\u014E'; + + /// + /// LATIN CAPITAL LETTER O WITH CIRCUMFLEX + /// + public const char Ocircumflex = '\u00D4'; + + /// + /// LATIN CAPITAL LETTER O WITH DIAERESIS + /// + public const char Odieresis = '\u00D6'; + + /// + /// LATIN CAPITAL LETTER O WITH GRAVE + /// + public const char Ograve = '\u00D2'; + + /// + /// LATIN CAPITAL LETTER O WITH HORN + /// + public const char Ohorn = '\u01A0'; + + /// + /// LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + /// + public const char Ohungarumlaut = '\u0150'; + + /// + /// LATIN CAPITAL LETTER O WITH MACRON + /// + public const char Omacron = '\u014C'; + + /// + /// OHM SIGN + /// + public const char Omega = '\u2126'; + + /// + /// GREEK CAPITAL LETTER OMEGA WITH TONOS + /// + public const char Omegatonos = '\u038F'; + + /// + /// GREEK CAPITAL LETTER OMICRON + /// + public const char Omicron = '\u039F'; + + /// + /// GREEK CAPITAL LETTER OMICRON WITH TONOS + /// + public const char Omicrontonos = '\u038C'; + + /// + /// LATIN CAPITAL LETTER O WITH STROKE + /// + public const char Oslash = '\u00D8'; + + /// + /// LATIN CAPITAL LETTER O WITH STROKE AND ACUTE + /// + public const char Oslashacute = '\u01FE'; + + /// + /// LATIN CAPITAL LETTER O WITH TILDE + /// + public const char Otilde = '\u00D5'; + + /// + /// LATIN CAPITAL LETTER P + /// + public const char P = '\u0050'; + + /// + /// GREEK CAPITAL LETTER PHI + /// + public const char Phi = '\u03A6'; + + /// + /// GREEK CAPITAL LETTER PI + /// + public const char Pi = '\u03A0'; + + /// + /// GREEK CAPITAL LETTER PSI + /// + public const char Psi = '\u03A8'; + + /// + /// LATIN CAPITAL LETTER Q + /// + public const char Q = '\u0051'; + + /// + /// LATIN CAPITAL LETTER R + /// + public const char R = '\u0052'; + + /// + /// LATIN CAPITAL LETTER R WITH ACUTE + /// + public const char Racute = '\u0154'; + + /// + /// LATIN CAPITAL LETTER R WITH CARON + /// + public const char Rcaron = '\u0158'; + + /// + /// LATIN CAPITAL LETTER R WITH CEDILLA + /// + public const char Rcommaaccent = '\u0156'; + + /// + /// BLACK-LETTER CAPITAL R + /// + public const char Rfraktur = '\u211C'; + + /// + /// GREEK CAPITAL LETTER RHO + /// + public const char Rho = '\u03A1'; + + /// + /// LATIN CAPITAL LETTER S + /// + public const char S = '\u0053'; + + /// + /// BOX DRAWINGS LIGHT DOWN AND RIGHT + /// + public const char SF010000 = '\u250C'; + + /// + /// BOX DRAWINGS LIGHT UP AND RIGHT + /// + public const char SF020000 = '\u2514'; + + /// + /// BOX DRAWINGS LIGHT DOWN AND LEFT + /// + public const char SF030000 = '\u2510'; + + /// + /// BOX DRAWINGS LIGHT UP AND LEFT + /// + public const char SF040000 = '\u2518'; + + /// + /// BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + /// + public const char SF050000 = '\u253C'; + + /// + /// BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + /// + public const char SF060000 = '\u252C'; + + /// + /// BOX DRAWINGS LIGHT UP AND HORIZONTAL + /// + public const char SF070000 = '\u2534'; + + /// + /// BOX DRAWINGS LIGHT VERTICAL AND RIGHT + /// + public const char SF080000 = '\u251C'; + + /// + /// BOX DRAWINGS LIGHT VERTICAL AND LEFT + /// + public const char SF090000 = '\u2524'; + + /// + /// BOX DRAWINGS LIGHT HORIZONTAL + /// + public const char SF100000 = '\u2500'; + + /// + /// BOX DRAWINGS LIGHT VERTICAL + /// + public const char SF110000 = '\u2502'; + + /// + /// BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + /// + public const char SF190000 = '\u2561'; + + /// + /// BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + /// + public const char SF200000 = '\u2562'; + + /// + /// BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + /// + public const char SF210000 = '\u2556'; + + /// + /// BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + /// + public const char SF220000 = '\u2555'; + + /// + /// BOX DRAWINGS DOUBLE VERTICAL AND LEFT + /// + public const char SF230000 = '\u2563'; + + /// + /// BOX DRAWINGS DOUBLE VERTICAL + /// + public const char SF240000 = '\u2551'; + + /// + /// BOX DRAWINGS DOUBLE DOWN AND LEFT + /// + public const char SF250000 = '\u2557'; + + /// + /// BOX DRAWINGS DOUBLE UP AND LEFT + /// + public const char SF260000 = '\u255D'; + + /// + /// BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + /// + public const char SF270000 = '\u255C'; + + /// + /// BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + /// + public const char SF280000 = '\u255B'; + + /// + /// BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + /// + public const char SF360000 = '\u255E'; + + /// + /// BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + /// + public const char SF370000 = '\u255F'; + + /// + /// BOX DRAWINGS DOUBLE UP AND RIGHT + /// + public const char SF380000 = '\u255A'; + + /// + /// BOX DRAWINGS DOUBLE DOWN AND RIGHT + /// + public const char SF390000 = '\u2554'; + + /// + /// BOX DRAWINGS DOUBLE UP AND HORIZONTAL + /// + public const char SF400000 = '\u2569'; + + /// + /// BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + /// + public const char SF410000 = '\u2566'; + + /// + /// BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + /// + public const char SF420000 = '\u2560'; + + /// + /// BOX DRAWINGS DOUBLE HORIZONTAL + /// + public const char SF430000 = '\u2550'; + + /// + /// BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + /// + public const char SF440000 = '\u256C'; + + /// + /// BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + /// + public const char SF450000 = '\u2567'; + + /// + /// BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + /// + public const char SF460000 = '\u2568'; + + /// + /// BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + /// + public const char SF470000 = '\u2564'; + + /// + /// BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + /// + public const char SF480000 = '\u2565'; + + /// + /// BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + /// + public const char SF490000 = '\u2559'; + + /// + /// BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + /// + public const char SF500000 = '\u2558'; + + /// + /// BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + /// + public const char SF510000 = '\u2552'; + + /// + /// BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + /// + public const char SF520000 = '\u2553'; + + /// + /// BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + /// + public const char SF530000 = '\u256B'; + + /// + /// BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + /// + public const char SF540000 = '\u256A'; + + /// + /// LATIN CAPITAL LETTER S WITH ACUTE + /// + public const char Sacute = '\u015A'; + + /// + /// LATIN CAPITAL LETTER S WITH CARON + /// + public const char Scaron = '\u0160'; + + /// + /// LATIN CAPITAL LETTER S WITH CEDILLA + /// + public const char Scedilla = '\u015E'; + + /// + /// LATIN CAPITAL LETTER S WITH CIRCUMFLEX + /// + public const char Scircumflex = '\u015C'; + + /// + /// LATIN CAPITAL LETTER S WITH COMMA BELOW + /// + public const char Scommaaccent = '\u0218'; + + /// + /// GREEK CAPITAL LETTER SIGMA + /// + public const char Sigma = '\u03A3'; + + /// + /// LATIN CAPITAL LETTER T + /// + public const char T = '\u0054'; + + /// + /// GREEK CAPITAL LETTER TAU + /// + public const char Tau = '\u03A4'; + + /// + /// LATIN CAPITAL LETTER T WITH STROKE + /// + public const char Tbar = '\u0166'; + + /// + /// LATIN CAPITAL LETTER T WITH CARON + /// + public const char Tcaron = '\u0164'; + + /// + /// LATIN CAPITAL LETTER T WITH CEDILLA + /// + public const char Tcommaaccent = '\u0162'; + + /// + /// GREEK CAPITAL LETTER THETA + /// + public const char Theta = '\u0398'; + + /// + /// LATIN CAPITAL LETTER THORN + /// + public const char Thorn = '\u00DE'; + + /// + /// LATIN CAPITAL LETTER U + /// + public const char U = '\u0055'; + + /// + /// LATIN CAPITAL LETTER U WITH ACUTE + /// + public const char Uacute = '\u00DA'; + + /// + /// LATIN CAPITAL LETTER U WITH BREVE + /// + public const char Ubreve = '\u016C'; + + /// + /// LATIN CAPITAL LETTER U WITH CIRCUMFLEX + /// + public const char Ucircumflex = '\u00DB'; + + /// + /// LATIN CAPITAL LETTER U WITH DIAERESIS + /// + public const char Udieresis = '\u00DC'; + + /// + /// LATIN CAPITAL LETTER U WITH GRAVE + /// + public const char Ugrave = '\u00D9'; + + /// + /// LATIN CAPITAL LETTER U WITH HORN + /// + public const char Uhorn = '\u01AF'; + + /// + /// LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + /// + public const char Uhungarumlaut = '\u0170'; + + /// + /// LATIN CAPITAL LETTER U WITH MACRON + /// + public const char Umacron = '\u016A'; + + /// + /// LATIN CAPITAL LETTER U WITH OGONEK + /// + public const char Uogonek = '\u0172'; + + /// + /// GREEK CAPITAL LETTER UPSILON + /// + public const char Upsilon = '\u03A5'; + + /// + /// GREEK UPSILON WITH HOOK SYMBOL + /// + public const char Upsilon1 = '\u03D2'; + + /// + /// GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + /// + public const char Upsilondieresis = '\u03AB'; + + /// + /// GREEK CAPITAL LETTER UPSILON WITH TONOS + /// + public const char Upsilontonos = '\u038E'; + + /// + /// LATIN CAPITAL LETTER U WITH RING ABOVE + /// + public const char Uring = '\u016E'; + + /// + /// LATIN CAPITAL LETTER U WITH TILDE + /// + public const char Utilde = '\u0168'; + + /// + /// LATIN CAPITAL LETTER V + /// + public const char V = '\u0056'; + + /// + /// LATIN CAPITAL LETTER W + /// + public const char W = '\u0057'; + + /// + /// LATIN CAPITAL LETTER W WITH ACUTE + /// + public const char Wacute = '\u1E82'; + + /// + /// LATIN CAPITAL LETTER W WITH CIRCUMFLEX + /// + public const char Wcircumflex = '\u0174'; + + /// + /// LATIN CAPITAL LETTER W WITH DIAERESIS + /// + public const char Wdieresis = '\u1E84'; + + /// + /// LATIN CAPITAL LETTER W WITH GRAVE + /// + public const char Wgrave = '\u1E80'; + + /// + /// LATIN CAPITAL LETTER X + /// + public const char X = '\u0058'; + + /// + /// GREEK CAPITAL LETTER XI + /// + public const char Xi = '\u039E'; + + /// + /// LATIN CAPITAL LETTER Y + /// + public const char Y = '\u0059'; + + /// + /// LATIN CAPITAL LETTER Y WITH ACUTE + /// + public const char Yacute = '\u00DD'; + + /// + /// LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + /// + public const char Ycircumflex = '\u0176'; + + /// + /// LATIN CAPITAL LETTER Y WITH DIAERESIS + /// + public const char Ydieresis = '\u0178'; + + /// + /// LATIN CAPITAL LETTER Y WITH GRAVE + /// + public const char Ygrave = '\u1EF2'; + + /// + /// LATIN CAPITAL LETTER Z + /// + public const char Z = '\u005A'; + + /// + /// LATIN CAPITAL LETTER Z WITH ACUTE + /// + public const char Zacute = '\u0179'; + + /// + /// LATIN CAPITAL LETTER Z WITH CARON + /// + public const char Zcaron = '\u017D'; + + /// + /// LATIN CAPITAL LETTER Z WITH DOT ABOVE + /// + public const char Zdotaccent = '\u017B'; + + /// + /// GREEK CAPITAL LETTER ZETA + /// + public const char Zeta = '\u0396'; + + /// + /// LATIN SMALL LETTER A + /// + public const char a = '\u0061'; + + /// + /// LATIN SMALL LETTER A WITH ACUTE + /// + public const char aacute = '\u00E1'; + + /// + /// LATIN SMALL LETTER A WITH BREVE + /// + public const char abreve = '\u0103'; + + /// + /// LATIN SMALL LETTER A WITH CIRCUMFLEX + /// + public const char acircumflex = '\u00E2'; + + /// + /// ACUTE ACCENT + /// + public const char acute = '\u00B4'; + + /// + /// COMBINING ACUTE ACCENT + /// + public const char acutecomb = '\u0301'; + + /// + /// LATIN SMALL LETTER A WITH DIAERESIS + /// + public const char adieresis = '\u00E4'; + + /// + /// LATIN SMALL LETTER AE + /// + public const char ae = '\u00E6'; + + /// + /// LATIN SMALL LETTER AE WITH ACUTE + /// + public const char aeacute = '\u01FD'; + + /// + /// HORIZONTAL BAR + /// + public const char afii00208 = '\u2015'; + + /// + /// CYRILLIC CAPITAL LETTER A + /// + public const char afii10017 = '\u0410'; + + /// + /// CYRILLIC CAPITAL LETTER BE + /// + public const char afii10018 = '\u0411'; + + /// + /// CYRILLIC CAPITAL LETTER VE + /// + public const char afii10019 = '\u0412'; + + /// + /// CYRILLIC CAPITAL LETTER GHE + /// + public const char afii10020 = '\u0413'; + + /// + /// CYRILLIC CAPITAL LETTER DE + /// + public const char afii10021 = '\u0414'; + + /// + /// CYRILLIC CAPITAL LETTER IE + /// + public const char afii10022 = '\u0415'; + + /// + /// CYRILLIC CAPITAL LETTER IO + /// + public const char afii10023 = '\u0401'; + + /// + /// CYRILLIC CAPITAL LETTER ZHE + /// + public const char afii10024 = '\u0416'; + + /// + /// CYRILLIC CAPITAL LETTER ZE + /// + public const char afii10025 = '\u0417'; + + /// + /// CYRILLIC CAPITAL LETTER I + /// + public const char afii10026 = '\u0418'; + + /// + /// CYRILLIC CAPITAL LETTER SHORT I + /// + public const char afii10027 = '\u0419'; + + /// + /// CYRILLIC CAPITAL LETTER KA + /// + public const char afii10028 = '\u041A'; + + /// + /// CYRILLIC CAPITAL LETTER EL + /// + public const char afii10029 = '\u041B'; + + /// + /// CYRILLIC CAPITAL LETTER EM + /// + public const char afii10030 = '\u041C'; + + /// + /// CYRILLIC CAPITAL LETTER EN + /// + public const char afii10031 = '\u041D'; + + /// + /// CYRILLIC CAPITAL LETTER O + /// + public const char afii10032 = '\u041E'; + + /// + /// CYRILLIC CAPITAL LETTER PE + /// + public const char afii10033 = '\u041F'; + + /// + /// CYRILLIC CAPITAL LETTER ER + /// + public const char afii10034 = '\u0420'; + + /// + /// CYRILLIC CAPITAL LETTER ES + /// + public const char afii10035 = '\u0421'; + + /// + /// CYRILLIC CAPITAL LETTER TE + /// + public const char afii10036 = '\u0422'; + + /// + /// CYRILLIC CAPITAL LETTER U + /// + public const char afii10037 = '\u0423'; + + /// + /// CYRILLIC CAPITAL LETTER EF + /// + public const char afii10038 = '\u0424'; + + /// + /// CYRILLIC CAPITAL LETTER HA + /// + public const char afii10039 = '\u0425'; + + /// + /// CYRILLIC CAPITAL LETTER TSE + /// + public const char afii10040 = '\u0426'; + + /// + /// CYRILLIC CAPITAL LETTER CHE + /// + public const char afii10041 = '\u0427'; + + /// + /// CYRILLIC CAPITAL LETTER SHA + /// + public const char afii10042 = '\u0428'; + + /// + /// CYRILLIC CAPITAL LETTER SHCHA + /// + public const char afii10043 = '\u0429'; + + /// + /// CYRILLIC CAPITAL LETTER HARD SIGN + /// + public const char afii10044 = '\u042A'; + + /// + /// CYRILLIC CAPITAL LETTER YERU + /// + public const char afii10045 = '\u042B'; + + /// + /// CYRILLIC CAPITAL LETTER SOFT SIGN + /// + public const char afii10046 = '\u042C'; + + /// + /// CYRILLIC CAPITAL LETTER E + /// + public const char afii10047 = '\u042D'; + + /// + /// CYRILLIC CAPITAL LETTER YU + /// + public const char afii10048 = '\u042E'; + + /// + /// CYRILLIC CAPITAL LETTER YA + /// + public const char afii10049 = '\u042F'; + + /// + /// CYRILLIC CAPITAL LETTER GHE WITH UPTURN + /// + public const char afii10050 = '\u0490'; + + /// + /// CYRILLIC CAPITAL LETTER DJE + /// + public const char afii10051 = '\u0402'; + + /// + /// CYRILLIC CAPITAL LETTER GJE + /// + public const char afii10052 = '\u0403'; + + /// + /// CYRILLIC CAPITAL LETTER UKRAINIAN IE + /// + public const char afii10053 = '\u0404'; + + /// + /// CYRILLIC CAPITAL LETTER DZE + /// + public const char afii10054 = '\u0405'; + + /// + /// CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + /// + public const char afii10055 = '\u0406'; + + /// + /// CYRILLIC CAPITAL LETTER YI + /// + public const char afii10056 = '\u0407'; + + /// + /// CYRILLIC CAPITAL LETTER JE + /// + public const char afii10057 = '\u0408'; + + /// + /// CYRILLIC CAPITAL LETTER LJE + /// + public const char afii10058 = '\u0409'; + + /// + /// CYRILLIC CAPITAL LETTER NJE + /// + public const char afii10059 = '\u040A'; + + /// + /// CYRILLIC CAPITAL LETTER TSHE + /// + public const char afii10060 = '\u040B'; + + /// + /// CYRILLIC CAPITAL LETTER KJE + /// + public const char afii10061 = '\u040C'; + + /// + /// CYRILLIC CAPITAL LETTER SHORT U + /// + public const char afii10062 = '\u040E'; + + /// + /// CYRILLIC SMALL LETTER A + /// + public const char afii10065 = '\u0430'; + + /// + /// CYRILLIC SMALL LETTER BE + /// + public const char afii10066 = '\u0431'; + + /// + /// CYRILLIC SMALL LETTER VE + /// + public const char afii10067 = '\u0432'; + + /// + /// CYRILLIC SMALL LETTER GHE + /// + public const char afii10068 = '\u0433'; + + /// + /// CYRILLIC SMALL LETTER DE + /// + public const char afii10069 = '\u0434'; + + /// + /// CYRILLIC SMALL LETTER IE + /// + public const char afii10070 = '\u0435'; + + /// + /// CYRILLIC SMALL LETTER IO + /// + public const char afii10071 = '\u0451'; + + /// + /// CYRILLIC SMALL LETTER ZHE + /// + public const char afii10072 = '\u0436'; + + /// + /// CYRILLIC SMALL LETTER ZE + /// + public const char afii10073 = '\u0437'; + + /// + /// CYRILLIC SMALL LETTER I + /// + public const char afii10074 = '\u0438'; + + /// + /// CYRILLIC SMALL LETTER SHORT I + /// + public const char afii10075 = '\u0439'; + + /// + /// CYRILLIC SMALL LETTER KA + /// + public const char afii10076 = '\u043A'; + + /// + /// CYRILLIC SMALL LETTER EL + /// + public const char afii10077 = '\u043B'; + + /// + /// CYRILLIC SMALL LETTER EM + /// + public const char afii10078 = '\u043C'; + + /// + /// CYRILLIC SMALL LETTER EN + /// + public const char afii10079 = '\u043D'; + + /// + /// CYRILLIC SMALL LETTER O + /// + public const char afii10080 = '\u043E'; + + /// + /// CYRILLIC SMALL LETTER PE + /// + public const char afii10081 = '\u043F'; + + /// + /// CYRILLIC SMALL LETTER ER + /// + public const char afii10082 = '\u0440'; + + /// + /// CYRILLIC SMALL LETTER ES + /// + public const char afii10083 = '\u0441'; + + /// + /// CYRILLIC SMALL LETTER TE + /// + public const char afii10084 = '\u0442'; + + /// + /// CYRILLIC SMALL LETTER U + /// + public const char afii10085 = '\u0443'; + + /// + /// CYRILLIC SMALL LETTER EF + /// + public const char afii10086 = '\u0444'; + + /// + /// CYRILLIC SMALL LETTER HA + /// + public const char afii10087 = '\u0445'; + + /// + /// CYRILLIC SMALL LETTER TSE + /// + public const char afii10088 = '\u0446'; + + /// + /// CYRILLIC SMALL LETTER CHE + /// + public const char afii10089 = '\u0447'; + + /// + /// CYRILLIC SMALL LETTER SHA + /// + public const char afii10090 = '\u0448'; + + /// + /// CYRILLIC SMALL LETTER SHCHA + /// + public const char afii10091 = '\u0449'; + + /// + /// CYRILLIC SMALL LETTER HARD SIGN + /// + public const char afii10092 = '\u044A'; + + /// + /// CYRILLIC SMALL LETTER YERU + /// + public const char afii10093 = '\u044B'; + + /// + /// CYRILLIC SMALL LETTER SOFT SIGN + /// + public const char afii10094 = '\u044C'; + + /// + /// CYRILLIC SMALL LETTER E + /// + public const char afii10095 = '\u044D'; + + /// + /// CYRILLIC SMALL LETTER YU + /// + public const char afii10096 = '\u044E'; + + /// + /// CYRILLIC SMALL LETTER YA + /// + public const char afii10097 = '\u044F'; + + /// + /// CYRILLIC SMALL LETTER GHE WITH UPTURN + /// + public const char afii10098 = '\u0491'; + + /// + /// CYRILLIC SMALL LETTER DJE + /// + public const char afii10099 = '\u0452'; + + /// + /// CYRILLIC SMALL LETTER GJE + /// + public const char afii10100 = '\u0453'; + + /// + /// CYRILLIC SMALL LETTER UKRAINIAN IE + /// + public const char afii10101 = '\u0454'; + + /// + /// CYRILLIC SMALL LETTER DZE + /// + public const char afii10102 = '\u0455'; + + /// + /// CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + /// + public const char afii10103 = '\u0456'; + + /// + /// CYRILLIC SMALL LETTER YI + /// + public const char afii10104 = '\u0457'; + + /// + /// CYRILLIC SMALL LETTER JE + /// + public const char afii10105 = '\u0458'; + + /// + /// CYRILLIC SMALL LETTER LJE + /// + public const char afii10106 = '\u0459'; + + /// + /// CYRILLIC SMALL LETTER NJE + /// + public const char afii10107 = '\u045A'; + + /// + /// CYRILLIC SMALL LETTER TSHE + /// + public const char afii10108 = '\u045B'; + + /// + /// CYRILLIC SMALL LETTER KJE + /// + public const char afii10109 = '\u045C'; + + /// + /// CYRILLIC SMALL LETTER SHORT U + /// + public const char afii10110 = '\u045E'; + + /// + /// CYRILLIC CAPITAL LETTER DZHE + /// + public const char afii10145 = '\u040F'; + + /// + /// CYRILLIC CAPITAL LETTER YAT + /// + public const char afii10146 = '\u0462'; + + /// + /// CYRILLIC CAPITAL LETTER FITA + /// + public const char afii10147 = '\u0472'; + + /// + /// CYRILLIC CAPITAL LETTER IZHITSA + /// + public const char afii10148 = '\u0474'; + + /// + /// CYRILLIC SMALL LETTER DZHE + /// + public const char afii10193 = '\u045F'; + + /// + /// CYRILLIC SMALL LETTER YAT + /// + public const char afii10194 = '\u0463'; + + /// + /// CYRILLIC SMALL LETTER FITA + /// + public const char afii10195 = '\u0473'; + + /// + /// CYRILLIC SMALL LETTER IZHITSA + /// + public const char afii10196 = '\u0475'; + + /// + /// CYRILLIC SMALL LETTER SCHWA + /// + public const char afii10846 = '\u04D9'; + + /// + /// LEFT-TO-RIGHT MARK + /// + public const char afii299 = '\u200E'; + + /// + /// RIGHT-TO-LEFT MARK + /// + public const char afii300 = '\u200F'; + + /// + /// ZERO WIDTH JOINER + /// + public const char afii301 = '\u200D'; + + /// + /// ARABIC PERCENT SIGN + /// + public const char afii57381 = '\u066A'; + + /// + /// ARABIC COMMA + /// + public const char afii57388 = '\u060C'; + + /// + /// ARABIC-INDIC DIGIT ZERO + /// + public const char afii57392 = '\u0660'; + + /// + /// ARABIC-INDIC DIGIT ONE + /// + public const char afii57393 = '\u0661'; + + /// + /// ARABIC-INDIC DIGIT TWO + /// + public const char afii57394 = '\u0662'; + + /// + /// ARABIC-INDIC DIGIT THREE + /// + public const char afii57395 = '\u0663'; + + /// + /// ARABIC-INDIC DIGIT FOUR + /// + public const char afii57396 = '\u0664'; + + /// + /// ARABIC-INDIC DIGIT FIVE + /// + public const char afii57397 = '\u0665'; + + /// + /// ARABIC-INDIC DIGIT SIX + /// + public const char afii57398 = '\u0666'; + + /// + /// ARABIC-INDIC DIGIT SEVEN + /// + public const char afii57399 = '\u0667'; + + /// + /// ARABIC-INDIC DIGIT EIGHT + /// + public const char afii57400 = '\u0668'; + + /// + /// ARABIC-INDIC DIGIT NINE + /// + public const char afii57401 = '\u0669'; + + /// + /// ARABIC SEMICOLON + /// + public const char afii57403 = '\u061B'; + + /// + /// ARABIC QUESTION MARK + /// + public const char afii57407 = '\u061F'; + + /// + /// ARABIC LETTER HAMZA + /// + public const char afii57409 = '\u0621'; + + /// + /// ARABIC LETTER ALEF WITH MADDA ABOVE + /// + public const char afii57410 = '\u0622'; + + /// + /// ARABIC LETTER ALEF WITH HAMZA ABOVE + /// + public const char afii57411 = '\u0623'; + + /// + /// ARABIC LETTER WAW WITH HAMZA ABOVE + /// + public const char afii57412 = '\u0624'; + + /// + /// ARABIC LETTER ALEF WITH HAMZA BELOW + /// + public const char afii57413 = '\u0625'; + + /// + /// ARABIC LETTER YEH WITH HAMZA ABOVE + /// + public const char afii57414 = '\u0626'; + + /// + /// ARABIC LETTER ALEF + /// + public const char afii57415 = '\u0627'; + + /// + /// ARABIC LETTER BEH + /// + public const char afii57416 = '\u0628'; + + /// + /// ARABIC LETTER TEH MARBUTA + /// + public const char afii57417 = '\u0629'; + + /// + /// ARABIC LETTER TEH + /// + public const char afii57418 = '\u062A'; + + /// + /// ARABIC LETTER THEH + /// + public const char afii57419 = '\u062B'; + + /// + /// ARABIC LETTER JEEM + /// + public const char afii57420 = '\u062C'; + + /// + /// ARABIC LETTER HAH + /// + public const char afii57421 = '\u062D'; + + /// + /// ARABIC LETTER KHAH + /// + public const char afii57422 = '\u062E'; + + /// + /// ARABIC LETTER DAL + /// + public const char afii57423 = '\u062F'; + + /// + /// ARABIC LETTER THAL + /// + public const char afii57424 = '\u0630'; + + /// + /// ARABIC LETTER REH + /// + public const char afii57425 = '\u0631'; + + /// + /// ARABIC LETTER ZAIN + /// + public const char afii57426 = '\u0632'; + + /// + /// ARABIC LETTER SEEN + /// + public const char afii57427 = '\u0633'; + + /// + /// ARABIC LETTER SHEEN + /// + public const char afii57428 = '\u0634'; + + /// + /// ARABIC LETTER SAD + /// + public const char afii57429 = '\u0635'; + + /// + /// ARABIC LETTER DAD + /// + public const char afii57430 = '\u0636'; + + /// + /// ARABIC LETTER TAH + /// + public const char afii57431 = '\u0637'; + + /// + /// ARABIC LETTER ZAH + /// + public const char afii57432 = '\u0638'; + + /// + /// ARABIC LETTER AIN + /// + public const char afii57433 = '\u0639'; + + /// + /// ARABIC LETTER GHAIN + /// + public const char afii57434 = '\u063A'; + + /// + /// ARABIC TATWEEL + /// + public const char afii57440 = '\u0640'; + + /// + /// ARABIC LETTER FEH + /// + public const char afii57441 = '\u0641'; + + /// + /// ARABIC LETTER QAF + /// + public const char afii57442 = '\u0642'; + + /// + /// ARABIC LETTER KAF + /// + public const char afii57443 = '\u0643'; + + /// + /// ARABIC LETTER LAM + /// + public const char afii57444 = '\u0644'; + + /// + /// ARABIC LETTER MEEM + /// + public const char afii57445 = '\u0645'; + + /// + /// ARABIC LETTER NOON + /// + public const char afii57446 = '\u0646'; + + /// + /// ARABIC LETTER WAW + /// + public const char afii57448 = '\u0648'; + + /// + /// ARABIC LETTER ALEF MAKSURA + /// + public const char afii57449 = '\u0649'; + + /// + /// ARABIC LETTER YEH + /// + public const char afii57450 = '\u064A'; + + /// + /// ARABIC FATHATAN + /// + public const char afii57451 = '\u064B'; + + /// + /// ARABIC DAMMATAN + /// + public const char afii57452 = '\u064C'; + + /// + /// ARABIC KASRATAN + /// + public const char afii57453 = '\u064D'; + + /// + /// ARABIC FATHA + /// + public const char afii57454 = '\u064E'; + + /// + /// ARABIC DAMMA + /// + public const char afii57455 = '\u064F'; + + /// + /// ARABIC KASRA + /// + public const char afii57456 = '\u0650'; + + /// + /// ARABIC SHADDA + /// + public const char afii57457 = '\u0651'; + + /// + /// ARABIC SUKUN + /// + public const char afii57458 = '\u0652'; + + /// + /// ARABIC LETTER HEH + /// + public const char afii57470 = '\u0647'; + + /// + /// ARABIC LETTER VEH + /// + public const char afii57505 = '\u06A4'; + + /// + /// ARABIC LETTER PEH + /// + public const char afii57506 = '\u067E'; + + /// + /// ARABIC LETTER TCHEH + /// + public const char afii57507 = '\u0686'; + + /// + /// ARABIC LETTER JEH + /// + public const char afii57508 = '\u0698'; + + /// + /// ARABIC LETTER GAF + /// + public const char afii57509 = '\u06AF'; + + /// + /// ARABIC LETTER TTEH + /// + public const char afii57511 = '\u0679'; + + /// + /// ARABIC LETTER DDAL + /// + public const char afii57512 = '\u0688'; + + /// + /// ARABIC LETTER RREH + /// + public const char afii57513 = '\u0691'; + + /// + /// ARABIC LETTER NOON GHUNNA + /// + public const char afii57514 = '\u06BA'; + + /// + /// ARABIC LETTER YEH BARREE + /// + public const char afii57519 = '\u06D2'; + + /// + /// ARABIC LETTER AE + /// + public const char afii57534 = '\u06D5'; + + /// + /// NEW SHEQEL SIGN + /// + public const char afii57636 = '\u20AA'; + + /// + /// HEBREW PUNCTUATION MAQAF + /// + public const char afii57645 = '\u05BE'; + + /// + /// HEBREW PUNCTUATION SOF PASUQ + /// + public const char afii57658 = '\u05C3'; + + /// + /// HEBREW LETTER ALEF + /// + public const char afii57664 = '\u05D0'; + + /// + /// HEBREW LETTER BET + /// + public const char afii57665 = '\u05D1'; + + /// + /// HEBREW LETTER GIMEL + /// + public const char afii57666 = '\u05D2'; + + /// + /// HEBREW LETTER DALET + /// + public const char afii57667 = '\u05D3'; + + /// + /// HEBREW LETTER HE + /// + public const char afii57668 = '\u05D4'; + + /// + /// HEBREW LETTER VAV + /// + public const char afii57669 = '\u05D5'; + + /// + /// HEBREW LETTER ZAYIN + /// + public const char afii57670 = '\u05D6'; + + /// + /// HEBREW LETTER HET + /// + public const char afii57671 = '\u05D7'; + + /// + /// HEBREW LETTER TET + /// + public const char afii57672 = '\u05D8'; + + /// + /// HEBREW LETTER YOD + /// + public const char afii57673 = '\u05D9'; + + /// + /// HEBREW LETTER FINAL KAF + /// + public const char afii57674 = '\u05DA'; + + /// + /// HEBREW LETTER KAF + /// + public const char afii57675 = '\u05DB'; + + /// + /// HEBREW LETTER LAMED + /// + public const char afii57676 = '\u05DC'; + + /// + /// HEBREW LETTER FINAL MEM + /// + public const char afii57677 = '\u05DD'; + + /// + /// HEBREW LETTER MEM + /// + public const char afii57678 = '\u05DE'; + + /// + /// HEBREW LETTER FINAL NUN + /// + public const char afii57679 = '\u05DF'; + + /// + /// HEBREW LETTER NUN + /// + public const char afii57680 = '\u05E0'; + + /// + /// HEBREW LETTER SAMEKH + /// + public const char afii57681 = '\u05E1'; + + /// + /// HEBREW LETTER AYIN + /// + public const char afii57682 = '\u05E2'; + + /// + /// HEBREW LETTER FINAL PE + /// + public const char afii57683 = '\u05E3'; + + /// + /// HEBREW LETTER PE + /// + public const char afii57684 = '\u05E4'; + + /// + /// HEBREW LETTER FINAL TSADI + /// + public const char afii57685 = '\u05E5'; + + /// + /// HEBREW LETTER TSADI + /// + public const char afii57686 = '\u05E6'; + + /// + /// HEBREW LETTER QOF + /// + public const char afii57687 = '\u05E7'; + + /// + /// HEBREW LETTER RESH + /// + public const char afii57688 = '\u05E8'; + + /// + /// HEBREW LETTER SHIN + /// + public const char afii57689 = '\u05E9'; + + /// + /// HEBREW LETTER TAV + /// + public const char afii57690 = '\u05EA'; + + /// + /// HEBREW LIGATURE YIDDISH DOUBLE VAV + /// + public const char afii57716 = '\u05F0'; + + /// + /// HEBREW LIGATURE YIDDISH VAV YOD + /// + public const char afii57717 = '\u05F1'; + + /// + /// HEBREW LIGATURE YIDDISH DOUBLE YOD + /// + public const char afii57718 = '\u05F2'; + + /// + /// HEBREW POINT HIRIQ + /// + public const char afii57793 = '\u05B4'; + + /// + /// HEBREW POINT TSERE + /// + public const char afii57794 = '\u05B5'; + + /// + /// HEBREW POINT SEGOL + /// + public const char afii57795 = '\u05B6'; + + /// + /// HEBREW POINT QUBUTS + /// + public const char afii57796 = '\u05BB'; + + /// + /// HEBREW POINT QAMATS + /// + public const char afii57797 = '\u05B8'; + + /// + /// HEBREW POINT PATAH + /// + public const char afii57798 = '\u05B7'; + + /// + /// HEBREW POINT SHEVA + /// + public const char afii57799 = '\u05B0'; + + /// + /// HEBREW POINT HATAF PATAH + /// + public const char afii57800 = '\u05B2'; + + /// + /// HEBREW POINT HATAF SEGOL + /// + public const char afii57801 = '\u05B1'; + + /// + /// HEBREW POINT HATAF QAMATS + /// + public const char afii57802 = '\u05B3'; + + /// + /// HEBREW POINT SIN DOT + /// + public const char afii57803 = '\u05C2'; + + /// + /// HEBREW POINT SHIN DOT + /// + public const char afii57804 = '\u05C1'; + + /// + /// HEBREW POINT HOLAM + /// + public const char afii57806 = '\u05B9'; + + /// + /// HEBREW POINT DAGESH OR MAPIQ + /// + public const char afii57807 = '\u05BC'; + + /// + /// HEBREW POINT METEG + /// + public const char afii57839 = '\u05BD'; + + /// + /// HEBREW POINT RAFE + /// + public const char afii57841 = '\u05BF'; + + /// + /// HEBREW PUNCTUATION PASEQ + /// + public const char afii57842 = '\u05C0'; + + /// + /// MODIFIER LETTER APOSTROPHE + /// + public const char afii57929 = '\u02BC'; + + /// + /// CARE OF + /// + public const char afii61248 = '\u2105'; + + /// + /// SCRIPT SMALL L + /// + public const char afii61289 = '\u2113'; + + /// + /// NUMERO SIGN + /// + public const char afii61352 = '\u2116'; + + /// + /// POP DIRECTIONAL FORMATTING + /// + public const char afii61573 = '\u202C'; + + /// + /// LEFT-TO-RIGHT OVERRIDE + /// + public const char afii61574 = '\u202D'; + + /// + /// RIGHT-TO-LEFT OVERRIDE + /// + public const char afii61575 = '\u202E'; + + /// + /// ZERO WIDTH NON-JOINER + /// + public const char afii61664 = '\u200C'; + + /// + /// ARABIC FIVE POINTED STAR + /// + public const char afii63167 = '\u066D'; + + /// + /// MODIFIER LETTER REVERSED COMMA + /// + public const char afii64937 = '\u02BD'; + + /// + /// LATIN SMALL LETTER A WITH GRAVE + /// + public const char agrave = '\u00E0'; + + /// + /// ALEF SYMBOL + /// + public const char aleph = '\u2135'; + + /// + /// GREEK SMALL LETTER ALPHA + /// + public const char alpha = '\u03B1'; + + /// + /// GREEK SMALL LETTER ALPHA WITH TONOS + /// + public const char alphatonos = '\u03AC'; + + /// + /// LATIN SMALL LETTER A WITH MACRON + /// + public const char amacron = '\u0101'; + + /// + /// AMPERSAND + /// + public const char ampersand = '\u0026'; + + /// + /// ANGLE + /// + public const char angle = '\u2220'; + + /// + /// LEFT-POINTING ANGLE BRACKET + /// + public const char angleleft = '\u2329'; + + /// + /// RIGHT-POINTING ANGLE BRACKET + /// + public const char angleright = '\u232A'; + + /// + /// GREEK ANO TELEIA + /// + public const char anoteleia = '\u0387'; + + /// + /// LATIN SMALL LETTER A WITH OGONEK + /// + public const char aogonek = '\u0105'; + + /// + /// ALMOST EQUAL TO + /// + public const char approxequal = '\u2248'; + + /// + /// LATIN SMALL LETTER A WITH RING ABOVE + /// + public const char aring = '\u00E5'; + + /// + /// LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE + /// + public const char aringacute = '\u01FB'; + + /// + /// LEFT RIGHT ARROW + /// + public const char arrowboth = '\u2194'; + + /// + /// LEFT RIGHT DOUBLE ARROW + /// + public const char arrowdblboth = '\u21D4'; + + /// + /// DOWNWARDS DOUBLE ARROW + /// + public const char arrowdbldown = '\u21D3'; + + /// + /// LEFTWARDS DOUBLE ARROW + /// + public const char arrowdblleft = '\u21D0'; + + /// + /// RIGHTWARDS DOUBLE ARROW + /// + public const char arrowdblright = '\u21D2'; + + /// + /// UPWARDS DOUBLE ARROW + /// + public const char arrowdblup = '\u21D1'; + + /// + /// DOWNWARDS ARROW + /// + public const char arrowdown = '\u2193'; + + /// + /// LEFTWARDS ARROW + /// + public const char arrowleft = '\u2190'; + + /// + /// RIGHTWARDS ARROW + /// + public const char arrowright = '\u2192'; + + /// + /// UPWARDS ARROW + /// + public const char arrowup = '\u2191'; + + /// + /// UP DOWN ARROW + /// + public const char arrowupdn = '\u2195'; + + /// + /// UP DOWN ARROW WITH BASE + /// + public const char arrowupdnbse = '\u21A8'; + + // EXTENDER + //public const char ARROW = '\u;arrowvertex;VERTICAL' + + /// + /// CIRCUMFLEX ACCENT + /// + public const char asciicircum = '\u005E'; + + /// + /// TILDE + /// + public const char asciitilde = '\u007E'; + + /// + /// ASTERISK + /// + public const char asterisk = '\u002A'; + + /// + /// ASTERISK OPERATOR + /// + public const char asteriskmath = '\u2217'; + + /// + /// COMMERCIAL AT + /// + public const char at = '\u0040'; + + /// + /// LATIN SMALL LETTER A WITH TILDE + /// + public const char atilde = '\u00E3'; + + /// + /// LATIN SMALL LETTER B + /// + public const char b = '\u0062'; + + /// + /// REVERSE SOLIDUS + /// + public const char backslash = '\u005C'; + + /// + /// VERTICAL LINE + /// + public const char bar = '\u007C'; + + /// + /// GREEK SMALL LETTER BETA + /// + public const char beta = '\u03B2'; + + /// + /// FULL BLOCK + /// + public const char block = '\u2588'; + + /// + /// LEFT CURLY BRACKET + /// + public const char braceleft = '\u007B'; + + /// + /// RIGHT CURLY BRACKET + /// + public const char braceright = '\u007D'; + + /// + /// LEFT SQUARE BRACKET + /// + public const char bracketleft = '\u005B'; + + /// + /// RIGHT SQUARE BRACKET + /// + public const char bracketright = '\u005D'; + + /// + /// BREVE + /// + public const char breve = '\u02D8'; + + /// + /// BROKEN BAR + /// + public const char brokenbar = '\u00A6'; + + /// + /// BULLET + /// + public const char bullet = '\u2022'; + + /// + /// LATIN SMALL LETTER C + /// + public const char c = '\u0063'; + + /// + /// LATIN SMALL LETTER C WITH ACUTE + /// + public const char cacute = '\u0107'; + + /// + /// CARON + /// + public const char caron = '\u02C7'; + + /// + /// DOWNWARDS ARROW WITH CORNER LEFTWARDS + /// + public const char carriagereturn = '\u21B5'; + + /// + /// LATIN SMALL LETTER C WITH CARON + /// + public const char ccaron = '\u010D'; + + /// + /// LATIN SMALL LETTER C WITH CEDILLA + /// + public const char ccedilla = '\u00E7'; + + /// + /// LATIN SMALL LETTER C WITH CIRCUMFLEX + /// + public const char ccircumflex = '\u0109'; + + /// + /// LATIN SMALL LETTER C WITH DOT ABOVE + /// + public const char cdotaccent = '\u010B'; + + /// + /// CEDILLA + /// + public const char cedilla = '\u00B8'; + + /// + /// CENT SIGN + /// + public const char cent = '\u00A2'; + + /// + /// GREEK SMALL LETTER CHI + /// + public const char chi = '\u03C7'; + + /// + /// WHITE CIRCLE + /// + public const char circle = '\u25CB'; + + /// + /// CIRCLED TIMES + /// + public const char circlemultiply = '\u2297'; + + /// + /// CIRCLED PLUS + /// + public const char circleplus = '\u2295'; + + /// + /// MODIFIER LETTER CIRCUMFLEX ACCENT + /// + public const char circumflex = '\u02C6'; + + /// + /// BLACK CLUB SUIT + /// + public const char club = '\u2663'; + + /// + /// COLON + /// + public const char colon = '\u003A'; + + /// + /// COLON SIGN + /// + public const char colonmonetary = '\u20A1'; + + /// + /// COMMA + /// + public const char comma = '\u002C'; + + /// + /// APPROXIMATELY EQUAL TO + /// + public const char congruent = '\u2245'; + + /// + /// COPYRIGHT SIGN + /// + public const char copyright = '\u00A9'; + + /// + /// CURRENCY SIGN + /// + public const char currency = '\u00A4'; + + /// + /// LATIN SMALL LETTER D + /// + public const char d = '\u0064'; + + /// + /// DAGGER + /// + public const char dagger = '\u2020'; + + /// + /// DOUBLE DAGGER + /// + public const char daggerdbl = '\u2021'; + + /// + /// LATIN SMALL LETTER D WITH CARON + /// + public const char dcaron = '\u010F'; + + /// + /// LATIN SMALL LETTER D WITH STROKE + /// + public const char dcroat = '\u0111'; + + /// + /// DEGREE SIGN + /// + public const char degree = '\u00B0'; + + /// + /// GREEK SMALL LETTER DELTA + /// + public const char delta = '\u03B4'; + + /// + /// BLACK DIAMOND SUIT + /// + public const char diamond = '\u2666'; + + /// + /// DIAERESIS + /// + public const char dieresis = '\u00A8'; + + /// + /// GREEK DIALYTIKA TONOS + /// + public const char dieresistonos = '\u0385'; + + /// + /// DIVISION SIGN + /// + public const char divide = '\u00F7'; + + /// + /// DARK SHADE + /// + public const char dkshade = '\u2593'; + + /// + /// LOWER HALF BLOCK + /// + public const char dnblock = '\u2584'; + + /// + /// DOLLAR SIGN + /// + public const char dollar = '\u0024'; + + /// + /// DONG SIGN + /// + public const char dong = '\u20AB'; + + /// + /// DOT ABOVE + /// + public const char dotaccent = '\u02D9'; + + /// + /// COMBINING DOT BELOW + /// + public const char dotbelowcomb = '\u0323'; + + /// + /// LATIN SMALL LETTER DOTLESS I + /// + public const char dotlessi = '\u0131'; + + /// + /// DOT OPERATOR + /// + public const char dotmath = '\u22C5'; + + /// + /// LATIN SMALL LETTER E + /// + public const char e = '\u0065'; + + /// + /// LATIN SMALL LETTER E WITH ACUTE + /// + public const char eacute = '\u00E9'; + + /// + /// LATIN SMALL LETTER E WITH BREVE + /// + public const char ebreve = '\u0115'; + + /// + /// LATIN SMALL LETTER E WITH CARON + /// + public const char ecaron = '\u011B'; + + /// + /// LATIN SMALL LETTER E WITH CIRCUMFLEX + /// + public const char ecircumflex = '\u00EA'; + + /// + /// LATIN SMALL LETTER E WITH DIAERESIS + /// + public const char edieresis = '\u00EB'; + + /// + /// LATIN SMALL LETTER E WITH DOT ABOVE + /// + public const char edotaccent = '\u0117'; + + /// + /// LATIN SMALL LETTER E WITH GRAVE + /// + public const char egrave = '\u00E8'; + + /// + /// DIGIT EIGHT + /// + public const char eight = '\u0038'; + + /// + /// ELEMENT OF + /// + public const char element = '\u2208'; + + /// + /// HORIZONTAL ELLIPSIS + /// + public const char ellipsis = '\u2026'; + + /// + /// LATIN SMALL LETTER E WITH MACRON + /// + public const char emacron = '\u0113'; + + /// + /// EM DASH + /// + public const char emdash = '\u2014'; + + /// + /// EMPTY SET + /// + public const char emptyset = '\u2205'; + + /// + /// EN DASH + /// + public const char endash = '\u2013'; + + /// + /// LATIN SMALL LETTER ENG + /// + public const char eng = '\u014B'; + + /// + /// LATIN SMALL LETTER E WITH OGONEK + /// + public const char eogonek = '\u0119'; + + /// + /// GREEK SMALL LETTER EPSILON + /// + public const char epsilon = '\u03B5'; + + /// + /// GREEK SMALL LETTER EPSILON WITH TONOS + /// + public const char epsilontonos = '\u03AD'; + + /// + /// EQUALS SIGN + /// + public const char equal = '\u003D'; + + /// + /// IDENTICAL TO + /// + public const char equivalence = '\u2261'; + + /// + /// ESTIMATED SYMBOL + /// + public const char estimated = '\u212E'; + + /// + /// GREEK SMALL LETTER ETA + /// + public const char eta = '\u03B7'; + + /// + /// GREEK SMALL LETTER ETA WITH TONOS + /// + public const char etatonos = '\u03AE'; + + /// + /// LATIN SMALL LETTER ETH + /// + public const char eth = '\u00F0'; + + /// + /// EXCLAMATION MARK + /// + public const char exclam = '\u0021'; + + /// + /// DOUBLE EXCLAMATION MARK + /// + public const char exclamdbl = '\u203C'; + + /// + /// INVERTED EXCLAMATION MARK + /// + public const char exclamdown = '\u00A1'; + + /// + /// THERE EXISTS + /// + public const char existential = '\u2203'; + + /// + /// LATIN SMALL LETTER F + /// + public const char f = '\u0066'; + + /// + /// FEMALE SIGN + /// + public const char female = '\u2640'; + + /// + /// FIGURE DASH + /// + public const char figuredash = '\u2012'; + + /// + /// BLACK SQUARE + /// + public const char filledbox = '\u25A0'; + + /// + /// BLACK RECTANGLE + /// + public const char filledrect = '\u25AC'; + + /// + /// DIGIT FIVE + /// + public const char five = '\u0035'; + + /// + /// VULGAR FRACTION FIVE EIGHTHS + /// + public const char fiveeighths = '\u215D'; + + /// + /// LATIN SMALL LETTER F WITH HOOK + /// + public const char florin = '\u0192'; + + /// + /// DIGIT FOUR + /// + public const char four = '\u0034'; + + /// + /// FRACTION SLASH + /// + public const char fraction = '\u2044'; + + /// + /// FRENCH FRANC SIGN + /// + public const char franc = '\u20A3'; + + /// + /// LATIN SMALL LETTER G + /// + public const char g = '\u0067'; + + /// + /// GREEK SMALL LETTER GAMMA + /// + public const char gamma = '\u03B3'; + + /// + /// LATIN SMALL LETTER G WITH BREVE + /// + public const char gbreve = '\u011F'; + + /// + /// LATIN SMALL LETTER G WITH CARON + /// + public const char gcaron = '\u01E7'; + + /// + /// LATIN SMALL LETTER G WITH CIRCUMFLEX + /// + public const char gcircumflex = '\u011D'; + + /// + /// LATIN SMALL LETTER G WITH CEDILLA + /// + public const char gcommaaccent = '\u0123'; + + /// + /// LATIN SMALL LETTER G WITH DOT ABOVE + /// + public const char gdotaccent = '\u0121'; + + /// + /// LATIN SMALL LETTER SHARP S + /// + public const char germandbls = '\u00DF'; + + /// + /// NABLA + /// + public const char gradient = '\u2207'; + + /// + /// GRAVE ACCENT + /// + public const char grave = '\u0060'; + + /// + /// COMBINING GRAVE ACCENT + /// + public const char gravecomb = '\u0300'; + + /// + /// GREATER-THAN SIGN + /// + public const char greater = '\u003E'; + + /// + /// GREATER-THAN OR EQUAL TO + /// + public const char greaterequal = '\u2265'; + + /// + /// LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + /// + public const char guillemotleft = '\u00AB'; + + /// + /// RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + /// + public const char guillemotright = '\u00BB'; + + /// + /// SINGLE LEFT-POINTING ANGLE QUOTATION MARK + /// + public const char guilsinglleft = '\u2039'; + + /// + /// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + /// + public const char guilsinglright = '\u203A'; + + /// + /// LATIN SMALL LETTER H + /// + public const char h = '\u0068'; + + /// + /// LATIN SMALL LETTER H WITH STROKE + /// + public const char hbar = '\u0127'; + + /// + /// LATIN SMALL LETTER H WITH CIRCUMFLEX + /// + public const char hcircumflex = '\u0125'; + + /// + /// BLACK HEART SUIT + /// + public const char heart = '\u2665'; + + /// + /// COMBINING HOOK ABOVE + /// + public const char hookabovecomb = '\u0309'; + + /// + /// HOUSE + /// + public const char house = '\u2302'; + + /// + /// DOUBLE ACUTE ACCENT + /// + public const char hungarumlaut = '\u02DD'; + + /// + /// HYPHEN-MINUS + /// + public const char hyphen = '\u002D'; + + /// + /// LATIN SMALL LETTER I + /// + public const char i = '\u0069'; + + /// + /// LATIN SMALL LETTER I WITH ACUTE + /// + public const char iacute = '\u00ED'; + + /// + /// LATIN SMALL LETTER I WITH BREVE + /// + public const char ibreve = '\u012D'; + + /// + /// LATIN SMALL LETTER I WITH CIRCUMFLEX + /// + public const char icircumflex = '\u00EE'; + + /// + /// LATIN SMALL LETTER I WITH DIAERESIS + /// + public const char idieresis = '\u00EF'; + + /// + /// LATIN SMALL LETTER I WITH GRAVE + /// + public const char igrave = '\u00EC'; + + /// + /// LATIN SMALL LIGATURE IJ + /// + public const char ij = '\u0133'; + + /// + /// LATIN SMALL LETTER I WITH MACRON + /// + public const char imacron = '\u012B'; + + /// + /// INFINITY + /// + public const char infinity = '\u221E'; + + /// + /// INTEGRAL + /// + public const char integral = '\u222B'; + + /// + /// BOTTOM HALF INTEGRAL + /// + public const char integralbt = '\u2321'; + + /// + /// TOP HALF INTEGRAL + /// + public const char integraltp = '\u2320'; + + /// + /// INTERSECTION + /// + public const char intersection = '\u2229'; + + /// + /// INVERSE BULLET + /// + public const char invbullet = '\u25D8'; + + /// + /// INVERSE WHITE CIRCLE + /// + public const char invcircle = '\u25D9'; + + /// + /// BLACK SMILING FACE + /// + public const char invsmileface = '\u263B'; + + /// + /// LATIN SMALL LETTER I WITH OGONEK + /// + public const char iogonek = '\u012F'; + + /// + /// GREEK SMALL LETTER IOTA + /// + public const char iota = '\u03B9'; + + /// + /// GREEK SMALL LETTER IOTA WITH DIALYTIKA + /// + public const char iotadieresis = '\u03CA'; + + /// + /// GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + /// + public const char iotadieresistonos = '\u0390'; + + /// + /// GREEK SMALL LETTER IOTA WITH TONOS + /// + public const char iotatonos = '\u03AF'; + + /// + /// LATIN SMALL LETTER I WITH TILDE + /// + public const char itilde = '\u0129'; + + /// + /// LATIN SMALL LETTER J + /// + public const char j = '\u006A'; + + /// + /// LATIN SMALL LETTER J WITH CIRCUMFLEX + /// + public const char jcircumflex = '\u0135'; + + /// + /// LATIN SMALL LETTER K + /// + public const char k = '\u006B'; + + /// + /// GREEK SMALL LETTER KAPPA + /// + public const char kappa = '\u03BA'; + + /// + /// LATIN SMALL LETTER K WITH CEDILLA + /// + public const char kcommaaccent = '\u0137'; + + /// + /// LATIN SMALL LETTER KRA + /// + public const char kgreenlandic = '\u0138'; + + /// + /// LATIN SMALL LETTER L + /// + public const char l = '\u006C'; + + /// + /// LATIN SMALL LETTER L WITH ACUTE + /// + public const char lacute = '\u013A'; + + /// + /// GREEK SMALL LETTER LAMDA + /// + public const char lambda = '\u03BB'; + + /// + /// LATIN SMALL LETTER L WITH CARON + /// + public const char lcaron = '\u013E'; + + /// + /// LATIN SMALL LETTER L WITH CEDILLA + /// + public const char lcommaaccent = '\u013C'; + + /// + /// LATIN SMALL LETTER L WITH MIDDLE DOT + /// + public const char ldot = '\u0140'; + + /// + /// LESS-THAN SIGN + /// + public const char less = '\u003C'; + + /// + /// LESS-THAN OR EQUAL TO + /// + public const char lessequal = '\u2264'; + + /// + /// LEFT HALF BLOCK + /// + public const char lfblock = '\u258C'; + + /// + /// LIRA SIGN + /// + public const char lira = '\u20A4'; + + /// + /// LOGICAL AND + /// + public const char logicaland = '\u2227'; + + /// + /// NOT SIGN + /// + public const char logicalnot = '\u00AC'; + + /// + /// LOGICAL OR + /// + public const char logicalor = '\u2228'; + + /// + /// LATIN SMALL LETTER LONG S + /// + public const char longs = '\u017F'; + + /// + /// LOZENGE + /// + public const char lozenge = '\u25CA'; + + /// + /// LATIN SMALL LETTER L WITH STROKE + /// + public const char lslash = '\u0142'; + + /// + /// LIGHT SHADE + /// + public const char ltshade = '\u2591'; + + /// + /// LATIN SMALL LETTER M + /// + public const char m = '\u006D'; + + /// + /// MACRON + /// + public const char macron = '\u00AF'; + + /// + /// MALE SIGN + /// + public const char male = '\u2642'; + + /// + /// MINUS SIGN + /// + public const char minus = '\u2212'; + + /// + /// PRIME + /// + public const char minute = '\u2032'; + + /// + /// MICRO SIGN + /// + public const char mu = '\u00B5'; + + /// + /// MULTIPLICATION SIGN + /// + public const char multiply = '\u00D7'; + + /// + /// EIGHTH NOTE + /// + public const char musicalnote = '\u266A'; + + /// + /// BEAMED EIGHTH NOTES + /// + public const char musicalnotedbl = '\u266B'; + + /// + /// LATIN SMALL LETTER N + /// + public const char n = '\u006E'; + + /// + /// LATIN SMALL LETTER N WITH ACUTE + /// + public const char nacute = '\u0144'; + + /// + /// LATIN SMALL LETTER N PRECEDED BY APOSTROPHE + /// + public const char napostrophe = '\u0149'; + + /// + /// LATIN SMALL LETTER N WITH CARON + /// + public const char ncaron = '\u0148'; + + /// + /// LATIN SMALL LETTER N WITH CEDILLA + /// + public const char ncommaaccent = '\u0146'; + + /// + /// DIGIT NINE + /// + public const char nine = '\u0039'; + + /// + /// NOT AN ELEMENT OF + /// + public const char notelement = '\u2209'; + + /// + /// NOT EQUAL TO + /// + public const char notequal = '\u2260'; + + /// + /// NOT A SUBSET OF + /// + public const char notsubset = '\u2284'; + + /// + /// LATIN SMALL LETTER N WITH TILDE + /// + public const char ntilde = '\u00F1'; + + /// + /// GREEK SMALL LETTER NU + /// + public const char nu = '\u03BD'; + + /// + /// NUMBER SIGN + /// + public const char numbersign = '\u0023'; + + /// + /// LATIN SMALL LETTER O + /// + public const char o = '\u006F'; + + /// + /// LATIN SMALL LETTER O WITH ACUTE + /// + public const char oacute = '\u00F3'; + + /// + /// LATIN SMALL LETTER O WITH BREVE + /// + public const char obreve = '\u014F'; + + /// + /// LATIN SMALL LETTER O WITH CIRCUMFLEX + /// + public const char ocircumflex = '\u00F4'; + + /// + /// LATIN SMALL LETTER O WITH DIAERESIS + /// + public const char odieresis = '\u00F6'; + + /// + /// LATIN SMALL LIGATURE OE + /// + public const char oe = '\u0153'; + + /// + /// OGONEK + /// + public const char ogonek = '\u02DB'; + + /// + /// LATIN SMALL LETTER O WITH GRAVE + /// + public const char ograve = '\u00F2'; + + /// + /// LATIN SMALL LETTER O WITH HORN + /// + public const char ohorn = '\u01A1'; + + /// + /// LATIN SMALL LETTER O WITH DOUBLE ACUTE + /// + public const char ohungarumlaut = '\u0151'; + + /// + /// LATIN SMALL LETTER O WITH MACRON + /// + public const char omacron = '\u014D'; + + /// + /// GREEK SMALL LETTER OMEGA + /// + public const char omega = '\u03C9'; + + /// + /// GREEK PI SYMBOL + /// + public const char omega1 = '\u03D6'; + + /// + /// GREEK SMALL LETTER OMEGA WITH TONOS + /// + public const char omegatonos = '\u03CE'; + + /// + /// GREEK SMALL LETTER OMICRON + /// + public const char omicron = '\u03BF'; + + /// + /// GREEK SMALL LETTER OMICRON WITH TONOS + /// + public const char omicrontonos = '\u03CC'; + + /// + /// DIGIT ONE + /// + public const char one = '\u0031'; + + /// + /// ONE DOT LEADER + /// + public const char onedotenleader = '\u2024'; + + /// + /// VULGAR FRACTION ONE EIGHTH + /// + public const char oneeighth = '\u215B'; + + /// + /// VULGAR FRACTION ONE HALF + /// + public const char onehalf = '\u00BD'; + + /// + /// VULGAR FRACTION ONE QUARTER + /// + public const char onequarter = '\u00BC'; + + /// + /// VULGAR FRACTION ONE THIRD + /// + public const char onethird = '\u2153'; + + /// + /// WHITE BULLET + /// + public const char openbullet = '\u25E6'; + + /// + /// FEMININE ORDINAL INDICATOR + /// + public const char ordfeminine = '\u00AA'; + + /// + /// MASCULINE ORDINAL INDICATOR + /// + public const char ordmasculine = '\u00BA'; + + /// + /// RIGHT ANGLE + /// + public const char orthogonal = '\u221F'; + + /// + /// LATIN SMALL LETTER O WITH STROKE + /// + public const char oslash = '\u00F8'; + + /// + /// LATIN SMALL LETTER O WITH STROKE AND ACUTE + /// + public const char oslashacute = '\u01FF'; + + /// + /// LATIN SMALL LETTER O WITH TILDE + /// + public const char otilde = '\u00F5'; + + /// + /// LATIN SMALL LETTER P + /// + public const char p = '\u0070'; + + /// + /// PILCROW SIGN + /// + public const char paragraph = '\u00B6'; + + /// + /// LEFT PARENTHESIS + /// + public const char parenleft = '\u0028'; + + /// + /// RIGHT PARENTHESIS + /// + public const char parenright = '\u0029'; + + /// + /// PARTIAL DIFFERENTIAL + /// + public const char partialdiff = '\u2202'; + + /// + /// PERCENT SIGN + /// + public const char percent = '\u0025'; + + /// + /// FULL STOP + /// + public const char period = '\u002E'; + + /// + /// MIDDLE DOT + /// + public const char periodcentered = '\u00B7'; + + /// + /// UP TACK + /// + public const char perpendicular = '\u22A5'; + + /// + /// PER MILLE SIGN + /// + public const char perthousand = '\u2030'; + + /// + /// PESETA SIGN + /// + public const char peseta = '\u20A7'; + + /// + /// GREEK SMALL LETTER PHI + /// + public const char phi = '\u03C6'; + + /// + /// GREEK PHI SYMBOL + /// + public const char phi1 = '\u03D5'; + + /// + /// GREEK SMALL LETTER PI + /// + public const char pi = '\u03C0'; + + /// + /// PLUS SIGN + /// + public const char plus = '\u002B'; + + /// + /// PLUS-MINUS SIGN + /// + public const char plusminus = '\u00B1'; + + /// + /// PRESCRIPTION TAKE + /// + public const char prescription = '\u211E'; + + /// + /// N-ARY PRODUCT + /// + public const char product = '\u220F'; + + /// + /// SUBSET OF + /// + public const char propersubset = '\u2282'; + + /// + /// SUPERSET OF + /// + public const char propersuperset = '\u2283'; + + /// + /// PROPORTIONAL TO + /// + public const char proportional = '\u221D'; + + /// + /// GREEK SMALL LETTER PSI + /// + public const char psi = '\u03C8'; + + /// + /// LATIN SMALL LETTER Q + /// + public const char q = '\u0071'; + + /// + /// QUESTION MARK + /// + public const char question = '\u003F'; + + /// + /// INVERTED QUESTION MARK + /// + public const char questiondown = '\u00BF'; + + /// + /// QUOTATION MARK + /// + public const char quotedbl = '\u0022'; + + /// + /// DOUBLE LOW-9 QUOTATION MARK + /// + public const char quotedblbase = '\u201E'; + + /// + /// LEFT DOUBLE QUOTATION MARK + /// + public const char quotedblleft = '\u201C'; + + /// + /// RIGHT DOUBLE QUOTATION MARK + /// + public const char quotedblright = '\u201D'; + + /// + /// LEFT SINGLE QUOTATION MARK + /// + public const char quoteleft = '\u2018'; + + /// + /// SINGLE HIGH-REVERSED-9 QUOTATION MARK + /// + public const char quotereversed = '\u201B'; + + /// + /// RIGHT SINGLE QUOTATION MARK + /// + public const char quoteright = '\u2019'; + + /// + /// SINGLE LOW-9 QUOTATION MARK + /// + public const char quotesinglbase = '\u201A'; + + /// + /// APOSTROPHE + /// + public const char quotesingle = '\u0027'; + + /// + /// LATIN SMALL LETTER R + /// + public const char r = '\u0072'; + + /// + /// LATIN SMALL LETTER R WITH ACUTE + /// + public const char racute = '\u0155'; + + /// + /// SQUARE ROOT + /// + public const char radical = '\u221A'; + + /// + /// LATIN SMALL LETTER R WITH CARON + /// + public const char rcaron = '\u0159'; + + /// + /// LATIN SMALL LETTER R WITH CEDILLA + /// + public const char rcommaaccent = '\u0157'; + + /// + /// SUBSET OF OR EQUAL TO + /// + public const char reflexsubset = '\u2286'; + + /// + /// SUPERSET OF OR EQUAL TO + /// + public const char reflexsuperset = '\u2287'; + + /// + /// REGISTERED SIGN + /// + public const char registered = '\u00AE'; + + /// + /// REVERSED NOT SIGN + /// + public const char revlogicalnot = '\u2310'; + + /// + /// GREEK SMALL LETTER RHO + /// + public const char rho = '\u03C1'; + + /// + /// RING ABOVE + /// + public const char ring = '\u02DA'; + + /// + /// RIGHT HALF BLOCK + /// + public const char rtblock = '\u2590'; + + /// + /// LATIN SMALL LETTER S + /// + public const char s = '\u0073'; + + /// + /// LATIN SMALL LETTER S WITH ACUTE + /// + public const char sacute = '\u015B'; + + /// + /// LATIN SMALL LETTER S WITH CARON + /// + public const char scaron = '\u0161'; + + /// + /// LATIN SMALL LETTER S WITH CEDILLA + /// + public const char scedilla = '\u015F'; + + /// + /// LATIN SMALL LETTER S WITH CIRCUMFLEX + /// + public const char scircumflex = '\u015D'; + + /// + /// LATIN SMALL LETTER S WITH COMMA BELOW + /// + public const char scommaaccent = '\u0219'; + + /// + /// DOUBLE PRIME + /// + public const char second = '\u2033'; + + /// + /// SECTION SIGN + /// + public const char section = '\u00A7'; + + /// + /// SEMICOLON + /// + public const char semicolon = '\u003B'; + + /// + /// DIGIT SEVEN + /// + public const char seven = '\u0037'; + + /// + /// VULGAR FRACTION SEVEN EIGHTHS + /// + public const char seveneighths = '\u215E'; + + /// + /// MEDIUM SHADE + /// + public const char shade = '\u2592'; + + /// + /// GREEK SMALL LETTER SIGMA + /// + public const char sigma = '\u03C3'; + + /// + /// GREEK SMALL LETTER FINAL SIGMA + /// + public const char sigma1 = '\u03C2'; + + /// + /// TILDE OPERATOR + /// + public const char similar = '\u223C'; + + /// + /// DIGIT SIX + /// + public const char six = '\u0036'; + + /// + /// SOLIDUS + /// + public const char slash = '\u002F'; + + /// + /// WHITE SMILING FACE + /// + public const char smileface = '\u263A'; + + /// + /// SPACE + /// + public const char space = '\u0020'; + + /// + /// BLACK SPADE SUIT + /// + public const char spade = '\u2660'; + + /// + /// POUND SIGN + /// + public const char sterling = '\u00A3'; + + /// + /// CONTAINS AS MEMBER + /// + public const char suchthat = '\u220B'; + + /// + /// N-ARY SUMMATION + /// + public const char summation = '\u2211'; + + /// + /// WHITE SUN WITH RAYS + /// + public const char sun = '\u263C'; + + /// + /// LATIN SMALL LETTER T + /// + public const char t = '\u0074'; + + /// + /// GREEK SMALL LETTER TAU + /// + public const char tau = '\u03C4'; + + /// + /// LATIN SMALL LETTER T WITH STROKE + /// + public const char tbar = '\u0167'; + + /// + /// LATIN SMALL LETTER T WITH CARON + /// + public const char tcaron = '\u0165'; + + /// + /// LATIN SMALL LETTER T WITH CEDILLA + /// + public const char tcommaaccent = '\u0163'; + + /// + /// THEREFORE + /// + public const char therefore = '\u2234'; + + /// + /// GREEK SMALL LETTER THETA + /// + public const char theta = '\u03B8'; + + /// + /// GREEK THETA SYMBOL + /// + public const char theta1 = '\u03D1'; + + /// + /// LATIN SMALL LETTER THORN + /// + public const char thorn = '\u00FE'; + + /// + /// DIGIT THREE + /// + public const char three = '\u0033'; + + /// + /// VULGAR FRACTION THREE EIGHTHS + /// + public const char threeeighths = '\u215C'; + + /// + /// VULGAR FRACTION THREE QUARTERS + /// + public const char threequarters = '\u00BE'; + + /// + /// SMALL TILDE + /// + public const char tilde = '\u02DC'; + + /// + /// COMBINING TILDE + /// + public const char tildecomb = '\u0303'; + + /// + /// GREEK TONOS + /// + public const char tonos = '\u0384'; + + /// + /// TRADE MARK SIGN + /// + public const char trademark = '\u2122'; + + /// + /// BLACK DOWN-POINTING TRIANGLE + /// + public const char triagdn = '\u25BC'; + + /// + /// BLACK LEFT-POINTING POINTER + /// + public const char triaglf = '\u25C4'; + + /// + /// BLACK RIGHT-POINTING POINTER + /// + public const char triagrt = '\u25BA'; + + /// + /// BLACK UP-POINTING TRIANGLE + /// + public const char triagup = '\u25B2'; + + /// + /// DIGIT TWO + /// + public const char two = '\u0032'; + + /// + /// TWO DOT LEADER + /// + public const char twodotenleader = '\u2025'; + + /// + /// VULGAR FRACTION TWO THIRDS + /// + public const char twothirds = '\u2154'; + + /// + /// LATIN SMALL LETTER U + /// + public const char u = '\u0075'; + + /// + /// LATIN SMALL LETTER U WITH ACUTE + /// + public const char uacute = '\u00FA'; + + /// + /// LATIN SMALL LETTER U WITH BREVE + /// + public const char ubreve = '\u016D'; + + /// + /// LATIN SMALL LETTER U WITH CIRCUMFLEX + /// + public const char ucircumflex = '\u00FB'; + + /// + /// LATIN SMALL LETTER U WITH DIAERESIS + /// + public const char udieresis = '\u00FC'; + + /// + /// LATIN SMALL LETTER U WITH GRAVE + /// + public const char ugrave = '\u00F9'; + + /// + /// LATIN SMALL LETTER U WITH HORN + /// + public const char uhorn = '\u01B0'; + + /// + /// LATIN SMALL LETTER U WITH DOUBLE ACUTE + /// + public const char uhungarumlaut = '\u0171'; + + /// + /// LATIN SMALL LETTER U WITH MACRON + /// + public const char umacron = '\u016B'; + + /// + /// LOW LINE + /// + public const char underscore = '\u005F'; + + /// + /// DOUBLE LOW LINE + /// + public const char underscoredbl = '\u2017'; + + /// + /// UNION + /// + public const char union = '\u222A'; + + /// + /// FOR ALL + /// + public const char universal = '\u2200'; + + /// + /// LATIN SMALL LETTER U WITH OGONEK + /// + public const char uogonek = '\u0173'; + + /// + /// UPPER HALF BLOCK + /// + public const char upblock = '\u2580'; + + /// + /// GREEK SMALL LETTER UPSILON + /// + public const char upsilon = '\u03C5'; + + /// + /// GREEK SMALL LETTER UPSILON WITH DIALYTIKA + /// + public const char upsilondieresis = '\u03CB'; + + /// + /// GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + /// + public const char upsilondieresistonos = '\u03B0'; + + /// + /// GREEK SMALL LETTER UPSILON WITH TONOS + /// + public const char upsilontonos = '\u03CD'; + + /// + /// LATIN SMALL LETTER U WITH RING ABOVE + /// + public const char uring = '\u016F'; + + /// + /// LATIN SMALL LETTER U WITH TILDE + /// + public const char utilde = '\u0169'; + + /// + /// LATIN SMALL LETTER V + /// + public const char v = '\u0076'; + + /// + /// LATIN SMALL LETTER W + /// + public const char w = '\u0077'; + + /// + /// LATIN SMALL LETTER W WITH ACUTE + /// + public const char wacute = '\u1E83'; + + /// + /// LATIN SMALL LETTER W WITH CIRCUMFLEX + /// + public const char wcircumflex = '\u0175'; + + /// + /// LATIN SMALL LETTER W WITH DIAERESIS + /// + public const char wdieresis = '\u1E85'; + + /// + /// SCRIPT CAPITAL P + /// + public const char weierstrass = '\u2118'; + + /// + /// LATIN SMALL LETTER W WITH GRAVE + /// + public const char wgrave = '\u1E81'; + + /// + /// LATIN SMALL LETTER X + /// + public const char x = '\u0078'; + + /// + /// GREEK SMALL LETTER XI + /// + public const char xi = '\u03BE'; + + /// + /// LATIN SMALL LETTER Y + /// + public const char y = '\u0079'; + + /// + /// LATIN SMALL LETTER Y WITH ACUTE + /// + public const char yacute = '\u00FD'; + + /// + /// LATIN SMALL LETTER Y WITH CIRCUMFLEX + /// + public const char ycircumflex = '\u0177'; + + /// + /// LATIN SMALL LETTER Y WITH DIAERESIS + /// + public const char ydieresis = '\u00FF'; + + /// + /// YEN SIGN + /// + public const char yen = '\u00A5'; + + /// + /// LATIN SMALL LETTER Y WITH GRAVE + /// + public const char ygrave = '\u1EF3'; + + /// + /// LATIN SMALL LETTER Z + /// + public const char z = '\u007A'; + + /// + /// LATIN SMALL LETTER Z WITH ACUTE + /// + public const char zacute = '\u017A'; + + /// + /// LATIN SMALL LETTER Z WITH CARON + /// + public const char zcaron = '\u017E'; + + /// + /// LATIN SMALL LETTER Z WITH DOT ABOVE + /// + public const char zdotaccent = '\u017C'; + + /// + /// DIGIT ZERO + /// + public const char zero = '\u0030'; + + /// + /// GREEK SMALL LETTER ZETA + /// + public const char zeta = '\u03B6'; + } +#endif +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/CMapInfo.cs b/src/PDFsharp/src/PdfSharp/Fonts/CMapInfo.cs new file mode 100644 index 00000000..cefa1957 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/CMapInfo.cs @@ -0,0 +1,141 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Fonts +{ + /// + /// Helper class that determines the characters used in a particular font. + /// + internal class CMapInfo + { + public CMapInfo(OpenTypeDescriptor descriptor) + { + Debug.Assert(descriptor != null); + _descriptor = descriptor; + } + internal OpenTypeDescriptor _descriptor; + + /// + /// Adds the characters of the specified string to the hashtable. + /// + public void AddChars(string text) + { + if (text != null) + { + bool symbol = _descriptor.FontFace.cmap.symbol; + int length = text.Length; + for (int idx = 0; idx < length; idx++) + { + char ch = text[idx]; + if (!CharacterToGlyphIndex.ContainsKey(ch)) + { + char ch2 = ch; + if (symbol) + { + // Remap ch for symbol fonts. + ch2 = (char)(ch | (_descriptor.FontFace.os2.usFirstCharIndex & 0xFF00)); // @@@ refactor + } + int glyphIndex = _descriptor.CharCodeToGlyphIndex(ch2); + CharacterToGlyphIndex.Add(ch, glyphIndex); + GlyphIndices[glyphIndex] = null; + MinChar = (char)Math.Min(MinChar, ch); + MaxChar = (char)Math.Max(MaxChar, ch); + } + } + } + } + + /// + /// Adds the glyphIndices to the hashtable. + /// + public void AddGlyphIndices(string glyphIndices) + { + if (glyphIndices != null) + { + int length = glyphIndices.Length; + for (int idx = 0; idx < length; idx++) + { + int glyphIndex = glyphIndices[idx]; + GlyphIndices[glyphIndex] = null; + } + } + } + + /// + /// Adds a ANSI characters. + /// + internal void AddAnsiChars() + { + byte[] ansi = new byte[256 - 32]; + for (int idx = 0; idx < 256 - 32; idx++) + ansi[idx] = (byte)(idx + 32); +#if EDF_CORE + string text = null; // PdfEncoders.WinAnsiEncoding.GetString(ansi, 0, ansi.Length); +#else + string text = PdfEncoders.WinAnsiEncoding.GetString(ansi, 0, ansi.Length); +#endif + AddChars(text); + } + + internal bool Contains(char ch) + { + return CharacterToGlyphIndex.ContainsKey(ch); + } + + public char[] Chars + { + get + { + char[] chars = new char[CharacterToGlyphIndex.Count]; + CharacterToGlyphIndex.Keys.CopyTo(chars, 0); + Array.Sort(chars); + return chars; + } + } + + public int[] GetGlyphIndices() + { + int[] indices = new int[GlyphIndices.Count]; + GlyphIndices.Keys.CopyTo(indices, 0); + Array.Sort(indices); + return indices; + } + + public char MinChar = char.MaxValue; + public char MaxChar = char.MinValue; + public Dictionary CharacterToGlyphIndex = new Dictionary(); + public Dictionary GlyphIndices = new Dictionary(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs b/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs new file mode 100644 index 00000000..a80f4df1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/FontDescriptorCache.cs @@ -0,0 +1,174 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using PdfSharp.Drawing; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; + +namespace PdfSharp.Fonts +{ + /// + /// Global table of OpenType font descriptor objects. + /// + internal sealed class FontDescriptorCache + { + FontDescriptorCache() + { + _cache = new Dictionary(); + } + + ///// + ///// Gets the FontDescriptor identified by the specified FontSelector. If no such object + ///// exists, a new FontDescriptor is created and added to the stock. + ///// + //public static FontDescriptor GetOrCreateDescriptor_DEL-ETE(string familyName, XFontStyle stlye, OpenTypeFontface fontface) + //{ + // //FontSelector1 selector = new FontSelector1(familyName, stlye); + // string fontDescriptorKey = null; // FontDescriptor.ComputeKey(familyName, stlye); + // try + // { + // Lock.EnterFontFactory(); + // FontDescriptor descriptor; + // if (!Singleton._cache.TryGetValue(fontDescriptorKey, out descriptor)) + // { + // descriptor = new OpenTypeDescriptor(fontDescriptorKey, familyName, stlye, fontface, null); + // Singleton._cache.Add(fontDescriptorKey, descriptor); + // } + // return descriptor; + // } + // finally { Lock.ExitFontFactory(); } + //} + + /// + /// Gets the FontDescriptor identified by the specified XFont. If no such object + /// exists, a new FontDescriptor is created and added to the cache. + /// + public static FontDescriptor GetOrCreateDescriptorFor(XFont font) + { + if (font == null) + throw new ArgumentNullException("font"); + + //FontSelector1 selector = new FontSelector1(font); + string fontDescriptorKey = FontDescriptor.ComputeKey(font); + try + { + Lock.EnterFontFactory(); + FontDescriptor descriptor; + if (!Singleton._cache.TryGetValue(fontDescriptorKey, out descriptor)) + { + descriptor = new OpenTypeDescriptor(fontDescriptorKey, font); + Singleton._cache.Add(fontDescriptorKey, descriptor); + } + return descriptor; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Gets the FontDescriptor identified by the specified FontSelector. If no such object + /// exists, a new FontDescriptor is created and added to the stock. + /// + public static FontDescriptor GetOrCreateDescriptor(string fontFamilyName, XFontStyle style) + { + if (string.IsNullOrEmpty(fontFamilyName)) + throw new ArgumentNullException("fontFamilyName"); + + //FontSelector1 selector = new FontSelector1(fontFamilyName, style); + string fontDescriptorKey = FontDescriptor.ComputeKey(fontFamilyName, style); + try + { + Lock.EnterFontFactory(); + FontDescriptor descriptor; + if (!Singleton._cache.TryGetValue(fontDescriptorKey, out descriptor)) + { + XFont font = new XFont(fontFamilyName, 10, style); + descriptor = GetOrCreateDescriptorFor(font); + if (Singleton._cache.ContainsKey(fontDescriptorKey)) + Singleton.GetType(); + else + Singleton._cache.Add(fontDescriptorKey, descriptor); + } + return descriptor; + } + finally { Lock.ExitFontFactory(); } + } + + public static FontDescriptor GetOrCreateDescriptor(string idName, byte[] fontData) + { + //FontSelector1 selector = new FontSelector1(idName); + string fontDescriptorKey = FontDescriptor.ComputeKey(idName); + try + { + Lock.EnterFontFactory(); + FontDescriptor descriptor; + if (!Singleton._cache.TryGetValue(fontDescriptorKey, out descriptor)) + { + descriptor = GetOrCreateOpenTypeDescriptor(fontDescriptorKey, idName, fontData); + Singleton._cache.Add(fontDescriptorKey, descriptor); + } + return descriptor; + } + finally { Lock.ExitFontFactory(); } + } + + static OpenTypeDescriptor GetOrCreateOpenTypeDescriptor(string fontDescriptorKey, string idName, byte[] fontData) + { + return new OpenTypeDescriptor(fontDescriptorKey, idName, fontData); + } + + /// + /// Gets the singleton. + /// + static FontDescriptorCache Singleton + { + get + { + if (_singleton == null) + { + try + { + Lock.EnterFontFactory(); + if (_singleton == null) + _singleton = new FontDescriptorCache(); + } + finally { Lock.ExitFontFactory(); } + } + return _singleton; + } + } + static volatile FontDescriptorCache _singleton; + + /// + /// Maps font font descriptor key to font descriptor. + /// + readonly Dictionary _cache; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs b/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs new file mode 100644 index 00000000..32e9d564 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/FontFactory.cs @@ -0,0 +1,450 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Text; +#if CORE || GDI +using System.Drawing; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +#endif +#if WPF +using System.Windows; +using System.Windows.Media; +using System.Windows.Resources; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +using WpfTypeface = System.Windows.Media.Typeface; +#endif +using PdfSharp.Drawing; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Internal; + +#pragma warning disable 1591 +// ReSharper disable RedundantNameQualifier + +namespace PdfSharp.Fonts +{ + /// + /// Provides functionality to map a fontface request to a physical font. + /// + internal static class FontFactory + { + //// Suffix for internal face names to indicate that the font data comes from the platform + //// and not from the users font resolver. + //public const string PlatformTag = "platform:"; + + /// + /// Converts specified information about a required typeface into a specific font. + /// + /// Name of the font family. + /// The font resolving options. + /// Typeface key if already known by caller, null otherwise. + /// + /// Information about the typeface, or null if no typeface can be found. + /// + public static FontResolverInfo ResolveTypeface(string familyName, FontResolvingOptions fontResolvingOptions, string typefaceKey) + { + if (string.IsNullOrEmpty(typefaceKey)) + typefaceKey = XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions); + + try + { + Lock.EnterFontFactory(); + // Was this typeface requested before? + FontResolverInfo fontResolverInfo; + if (FontResolverInfosByName.TryGetValue(typefaceKey, out fontResolverInfo)) + return fontResolverInfo; + + // Case: This typeface was not yet resolved before. + + // Is there a custom font resolver available? + IFontResolver customFontResolver = GlobalFontSettings.FontResolver; + if (customFontResolver != null) + { + // Case: Use custom font resolver. + fontResolverInfo = customFontResolver.ResolveTypeface(familyName, fontResolvingOptions.IsBold, fontResolvingOptions.IsItalic); + + // If resolved by custom font resolver register info and font source. + if (fontResolverInfo != null && !(fontResolverInfo is PlatformFontResolverInfo)) + { + // OverrideStyleSimulations is true only for internal quality tests. + if (fontResolvingOptions.OverrideStyleSimulations) + { + // Override style simulation returned by custom font resolver. + fontResolverInfo = new FontResolverInfo(fontResolverInfo.FaceName, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, fontResolverInfo.CollectionNumber); + } + + string resolverInfoKey = fontResolverInfo.Key; + FontResolverInfo existingFontResolverInfo; + if (FontResolverInfosByName.TryGetValue(resolverInfoKey, out existingFontResolverInfo)) + { + // Case: A new typeface was resolved with the same info as a previous one. + // Discard new object an reuse previous one. + fontResolverInfo = existingFontResolverInfo; + // Associate with typeface key. + FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); +#if DEBUG + // The font source should exist. + Debug.Assert(FontSourcesByName.ContainsKey(fontResolverInfo.FaceName)); +#endif + } + else + { + // Case: No such font resolver info exists. + // Add to both dictionaries. + FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); + Debug.Assert(resolverInfoKey == fontResolverInfo.Key); + FontResolverInfosByName.Add(resolverInfoKey, fontResolverInfo); + + // Create font source if not yet exists. + XFontSource previousFontSource; + if (FontSourcesByName.TryGetValue(fontResolverInfo.FaceName, out previousFontSource)) + { + // Case: The font source exists, because a previous font resolver info comes + // with the same face name, but was different in style simulation flags. + // Nothing to do. + } + else + { + // Case: Get font from custom font resolver and create font source. + byte[] bytes = customFontResolver.GetFont(fontResolverInfo.FaceName); + XFontSource fontSource = XFontSource.GetOrCreateFrom(bytes); + + // Add font source's font resolver name if it is different to the face name. + if (string.Compare(fontResolverInfo.FaceName, fontSource.FontName, StringComparison.OrdinalIgnoreCase) != 0) + FontSourcesByName.Add(fontResolverInfo.FaceName, fontSource); + } + } + } + } + else + { + // Case: There was no custom font resolver set. + // Use platform font resolver. + // If it was successful resolver info and font source are cached + // automatically by PlatformFontResolver.ResolveTypeface. + fontResolverInfo = PlatformFontResolver.ResolveTypeface(familyName, fontResolvingOptions, typefaceKey); + } + + // Return value is null if the typeface could not be resolved. + // In this case PDFsharp stops. + return fontResolverInfo; + } + finally { Lock.ExitFontFactory(); } + } + +#if GDI + /// + /// Registers the font face. + /// + public static XFontSource RegisterFontFace(byte[] fontBytes) + { + try + { + Lock.EnterFontFactory(); + ulong key = FontHelper.CalcChecksum(fontBytes); + XFontSource fontSource; + if (FontSourcesByKey.TryGetValue(key, out fontSource)) + { + throw new InvalidOperationException("Font face already registered."); + } + fontSource = XFontSource.GetOrCreateFrom(fontBytes); + Debug.Assert(FontSourcesByKey.ContainsKey(key)); + Debug.Assert(fontSource.Fontface != null); + + //fontSource.Fontface = new OpenTypeFontface(fontSource); + //FontSourcesByKey.Add(checksum, fontSource); + //FontSourcesByFontName.Add(fontSource.FontName, fontSource); + + XGlyphTypeface glyphTypeface = new XGlyphTypeface(fontSource); + FontSourcesByName.Add(glyphTypeface.Key, fontSource); + GlyphTypefaceCache.AddGlyphTypeface(glyphTypeface); + return fontSource; + } + finally { Lock.ExitFontFactory(); } + } +#endif + + /// + /// Gets the bytes of a physical font with specified face name. + /// + public static XFontSource GetFontSourceByFontName(string fontName) + { + XFontSource fontSource; + if (FontSourcesByName.TryGetValue(fontName, out fontSource)) + return fontSource; + + Debug.Assert(false, string.Format("An XFontSource with the name '{0}' does not exists.", fontName)); + return null; + } + + /// + /// Gets the bytes of a physical font with specified face name. + /// + public static XFontSource GetFontSourceByTypefaceKey(string typefaceKey) + { + XFontSource fontSource; + if (FontSourcesByName.TryGetValue(typefaceKey, out fontSource)) + return fontSource; + + Debug.Assert(false, string.Format("An XFontSource with the typeface key '{0}' does not exists.", typefaceKey)); + return null; + } + + public static bool TryGetFontSourceByKey(ulong key, out XFontSource fontSource) + { + return FontSourcesByKey.TryGetValue(key, out fontSource); + } + + /// + /// Gets a value indicating whether at least one font source was created. + /// + public static bool HasFontSources + { + get { return FontSourcesByName.Count > 0; } + } + + public static bool TryGetFontResolverInfoByTypefaceKey(string typeFaceKey, out FontResolverInfo info) + { + return FontResolverInfosByName.TryGetValue(typeFaceKey, out info); + } + + public static bool TryGetFontSourceByTypefaceKey(string typefaceKey, out XFontSource source) + { + return FontSourcesByName.TryGetValue(typefaceKey, out source); + } + + //public static bool TryGetFontSourceByFaceName(string faceName, out XFontSource source) + //{ + // return FontSourcesByName.TryGetValue(faceName, out source); + //} + + internal static void CacheFontResolverInfo(string typefaceKey, FontResolverInfo fontResolverInfo) + { + FontResolverInfo existingfFontResolverInfo; + // Check whether identical font is already registered. + if (FontResolverInfosByName.TryGetValue(typefaceKey, out existingfFontResolverInfo)) + { + // Should never come here. + throw new InvalidOperationException(string.Format("A font file with different content already exists with the specified face name '{0}'.", typefaceKey)); + } + if (FontResolverInfosByName.TryGetValue(fontResolverInfo.Key, out existingfFontResolverInfo)) + { + // Should never come here. + throw new InvalidOperationException(string.Format("A font resolver already exists with the specified key '{0}'.", fontResolverInfo.Key)); + } + // Add to both dictionaries. + FontResolverInfosByName.Add(typefaceKey, fontResolverInfo); + FontResolverInfosByName.Add(fontResolverInfo.Key, fontResolverInfo); + } + + /// + /// Caches a font source under its face name and its key. + /// + public static XFontSource CacheFontSource(XFontSource fontSource) + { + try + { + Lock.EnterFontFactory(); + // Check whether an identical font source with a different face name already exists. + XFontSource existingFontSource; + if (FontSourcesByKey.TryGetValue(fontSource.Key, out existingFontSource)) + { +#if DEBUG + // Fonts have same length and check sum. Now check byte by byte identity. + int length = fontSource.Bytes.Length; + for (int idx = 0; idx < length; idx++) + { + if (existingFontSource.Bytes[idx] != fontSource.Bytes[idx]) + { + //Debug.Assert(false,"Two fonts with identical checksum found."); + break; + //goto FontsAreNotIdentical; + } + } + Debug.Assert(existingFontSource.Fontface != null); +#endif + return existingFontSource; + + //FontsAreNotIdentical: + //// Incredible rare case: Two different fonts have the same size and check sum. + //// Give the new one a new key until it do not clash with an existing one. + //while (FontSourcesByKey.ContainsKey(fontSource.Key)) + // fontSource.IncrementKey(); + } + + OpenTypeFontface fontface = fontSource.Fontface; + if (fontface == null) + { + // Create OpenType fontface for this font source. + fontSource.Fontface = new OpenTypeFontface(fontSource); + } + FontSourcesByKey.Add(fontSource.Key, fontSource); + FontSourcesByName.Add(fontSource.FontName, fontSource); + return fontSource; + } + finally { Lock.ExitFontFactory(); } + } + + /// + /// Caches a font source under its face name and its key. + /// + public static XFontSource CacheNewFontSource(string typefaceKey, XFontSource fontSource) + { + // Debug.Assert(!FontSourcesByFaceName.ContainsKey(fontSource.FaceName)); + + // Check whether an identical font source with a different face name already exists. + XFontSource existingFontSource; + if (FontSourcesByKey.TryGetValue(fontSource.Key, out existingFontSource)) + { + //// Fonts have same length and check sum. Now check byte by byte identity. + //int length = fontSource.Bytes.Length; + //for (int idx = 0; idx < length; idx++) + //{ + // if (existingFontSource.Bytes[idx] != fontSource.Bytes[idx]) + // { + // goto FontsAreNotIdentical; + // } + //} + return existingFontSource; + + ////// The bytes are really identical. Register font source again with the new face name + ////// but return the existing one to save memory. + ////FontSourcesByFaceName.Add(fontSource.FaceName, existingFontSource); + ////return existingFontSource; + + //FontsAreNotIdentical: + //// Incredible rare case: Two different fonts have the same size and check sum. + //// Give the new one a new key until it do not clash with an existing one. + //while (FontSourcesByKey.ContainsKey(fontSource.Key)) + // fontSource.IncrementKey(); + } + + OpenTypeFontface fontface = fontSource.Fontface; + if (fontface == null) + { + fontface = new OpenTypeFontface(fontSource); + fontSource.Fontface = fontface; // Also sets the font name in fontSource + } + + FontSourcesByName.Add(typefaceKey, fontSource); + FontSourcesByName.Add(fontSource.FontName, fontSource); + FontSourcesByKey.Add(fontSource.Key, fontSource); + + return fontSource; + } + + public static void CacheExistingFontSourceWithNewTypefaceKey(string typefaceKey, XFontSource fontSource) + { + try + { + Lock.EnterFontFactory(); + FontSourcesByName.Add(typefaceKey, fontSource); + } + finally { Lock.ExitFontFactory(); } + } + + internal static string GetFontCachesState() + { + StringBuilder state = new StringBuilder(); + string[] keys; + int count; + + // FontResolverInfo by name. + state.Append("====================\n"); + state.Append("Font resolver info by name\n"); + Dictionary.KeyCollection keyCollection = FontResolverInfosByName.Keys; + count = keyCollection.Count; + keys = new string[count]; + keyCollection.CopyTo(keys, 0); + Array.Sort(keys, StringComparer.OrdinalIgnoreCase); + foreach (string key in keys) + state.AppendFormat(" {0}: {1}\n", key, FontResolverInfosByName[key].DebuggerDisplay); + state.Append("\n"); + + // FontSource by key. + state.Append("Font source by key and name\n"); + Dictionary.KeyCollection fontSourceKeys = FontSourcesByKey.Keys; + count = fontSourceKeys.Count; + ulong[] ulKeys = new ulong[count]; + fontSourceKeys.CopyTo(ulKeys, 0); + Array.Sort(ulKeys, delegate (ulong x, ulong y) { return x == y ? 0 : (x > y ? 1 : -1); }); + foreach (ulong ul in ulKeys) + state.AppendFormat(" {0}: {1}\n", ul, FontSourcesByKey[ul].DebuggerDisplay); + Dictionary.KeyCollection fontSourceNames = FontSourcesByName.Keys; + count = fontSourceNames.Count; + keys = new string[count]; + fontSourceNames.CopyTo(keys, 0); + Array.Sort(keys, StringComparer.OrdinalIgnoreCase); + foreach (string key in keys) + state.AppendFormat(" {0}: {1}\n", key, FontSourcesByName[key].DebuggerDisplay); + state.Append("--------------------\n\n"); + + // FontFamilyInternal by name. + state.Append(FontFamilyCache.GetCacheState()); + // XGlyphTypeface by name. + state.Append(GlyphTypefaceCache.GetCacheState()); + // OpenTypeFontface by name. + state.Append(OpenTypeFontfaceCache.GetCacheState()); + return state.ToString(); + } + + // TODO: Move to ctor + + /// + /// Maps font typeface key to font resolver info. + /// + //static readonly Dictionary FontResolverInfosByTypefaceKey = new Dictionary(StringComparer.OrdinalIgnoreCase); + static readonly Dictionary FontResolverInfosByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + + ///// + ///// Maps font resolver info key to font resolver info. + ///// + //static readonly Dictionary FontResolverInfosByKey = new Dictionary(); + + /// + /// Maps typeface key or font name to font source. + /// + //static readonly Dictionary FontSourcesByTypefaceKey = new Dictionary(StringComparer.OrdinalIgnoreCase); + static readonly Dictionary FontSourcesByName = new Dictionary(StringComparer.OrdinalIgnoreCase); + + ///// + ///// Maps font name to font source. + ///// + //static readonly Dictionary FontSourcesByFontName = new Dictionary(); + + /// + /// Maps font source key to font source. + /// + static readonly Dictionary FontSourcesByKey = new Dictionary(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/FontResolverInfo.cs b/src/PDFsharp/src/PdfSharp/Fonts/FontResolverInfo.cs new file mode 100644 index 00000000..a3f27318 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/FontResolverInfo.cs @@ -0,0 +1,211 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Drawing; +#if CORE +using System.Drawing; +#endif +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Fonts +{ + // The English terms font, font family, typeface, glyph etc. are sometimes confusingly used. + // Here a short clarification by Wikipedia. + // + // Wikipedia EN -> DE + // Font -> Schriftschnitt + // Computer font -> Font (Informationstechnik) + // Typeface (Font family) -> Schriftart / Schriftfamilie + // Glyph -> Glyphe + // + // It seems that typeface and font family are synonyms in English. + // In WPF a family name is used as a term for a bunch of fonts that share the same + // characteristics, like Univers or Times New Roman. + // In WPF a fontface describes a request of a font of a particular font family, e.g. + // Univers medium bold italic. + // In WPF a glyph typeface is the result of requesting a typeface, i.e. a physical font + // plus the information whether bold and/or italic should be simulated. + // + // Wikipedia DE -> EN + // Schriftart -> Typeface + // Schriftschnitt -> Font + // Schriftfamilie -> ~ (means Font family) + // Schriftsippe -> Font superfamily + // Font -> Computer font + // + // http://en.wikipedia.org/wiki/Font + // http://en.wikipedia.org/wiki/Computer_font + // http://en.wikipedia.org/wiki/Typeface + // http://en.wikipedia.org/wiki/Glyph + // http://en.wikipedia.org/wiki/Typographic_unit + // + // FaceName: A unique and only internally used name of a glyph typeface. In other words the name of the font data that represents a specific font. + // + // + + /// + /// Describes the physical font that must be used to render a particular XFont. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public class FontResolverInfo + { + private const string KeyPrefix = "frik:"; // Font Resolver Info Key + + /// + /// Initializes a new instance of the struct. + /// + /// The name that uniquely identifies the fontface. + public FontResolverInfo(string faceName) : + this(faceName, false, false, 0) + { } + + /// + /// Initializes a new instance of the struct. + /// + /// The name that uniquely identifies the fontface. + /// Set to true to simulate bold when rendered. Not implemented and must be false. + /// Set to true to simulate italic when rendered. + /// Index of the font in a true type font collection. + /// Not yet implemented and must be zero. + /// + internal FontResolverInfo(string faceName, bool mustSimulateBold, bool mustSimulateItalic, int collectionNumber) + { + if (String.IsNullOrEmpty(faceName)) + throw new ArgumentNullException("faceName"); + if (collectionNumber != 0) + throw new NotImplementedException("collectionNumber is not yet implemented and must be 0."); + + _faceName = faceName; + _mustSimulateBold = mustSimulateBold; + _mustSimulateItalic = mustSimulateItalic; + _collectionNumber = collectionNumber; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The name that uniquely identifies the fontface. + /// Set to true to simulate bold when rendered. Not implemented and must be false. + /// Set to true to simulate italic when rendered. + public FontResolverInfo(string faceName, bool mustSimulateBold, bool mustSimulateItalic) + : this(faceName, mustSimulateBold, mustSimulateItalic, 0) + { } + + /// + /// Initializes a new instance of the struct. + /// + /// The name that uniquely identifies the fontface. + /// The style simulation flags. + public FontResolverInfo(string faceName, XStyleSimulations styleSimulations) + : this(faceName, + (styleSimulations & XStyleSimulations.BoldSimulation) == XStyleSimulations.BoldSimulation, + (styleSimulations & XStyleSimulations.ItalicSimulation) == XStyleSimulations.ItalicSimulation, 0) + { } + + /// + /// Gets the key for this object. + /// + internal string Key + { + get + { + return _key ?? (_key = KeyPrefix + _faceName.ToLowerInvariant() + + '/' + (_mustSimulateBold ? "b+" : "b-") + (_mustSimulateItalic ? "i+" : "i-")); + } + } + string _key; + + /// + /// A name that uniquely identifies the font (not the family), e.g. the file name of the font. PDFsharp does not use this + /// name internally, but passes it to the GetFont function of the IFontResolver interface to retrieve the font data. + /// + public string FaceName + { + get { return _faceName; } + } + readonly string _faceName; + + /// + /// Indicates whether bold must be simulated. Bold simulation is not implemented in PDFsharp. + /// + public bool MustSimulateBold + { + get { return _mustSimulateBold; } + } + readonly bool _mustSimulateBold; + + /// + /// Indicates whether italic must be simulated. + /// + public bool MustSimulateItalic + { + get { return _mustSimulateItalic; } + } + readonly bool _mustSimulateItalic; + + /// + /// Gets the style simulation flags. + /// + public XStyleSimulations StyleSimulations + { + get { return (_mustSimulateBold ? XStyleSimulations.BoldSimulation : 0) | (_mustSimulateItalic ? XStyleSimulations.ItalicSimulation : 0); } + } + + /// + /// The number of the font in a Truetype font collection file. The number of the first font is 0. + /// NOT YET IMPLEMENTED. Must be zero. + /// + internal int CollectionNumber // TODO : Find a better name. + { + get { return _collectionNumber; } + } + readonly int _collectionNumber; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + internal string DebuggerDisplay + { + get + { + return string.Format(CultureInfo.InvariantCulture, "FontResolverInfo: '{0}',{1}{2}", FaceName, + MustSimulateBold ? " simulate Bold" : "", + MustSimulateItalic ? " simulate Italic" : ""); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/FontResolvingOptions.cs b/src/PDFsharp/src/PdfSharp/Fonts/FontResolvingOptions.cs new file mode 100644 index 00000000..d14b6e0f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/FontResolvingOptions.cs @@ -0,0 +1,89 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Text; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; + +namespace PdfSharp.Fonts +{ + /// + /// Parameters that affect font selection. + /// + class FontResolvingOptions + { + public FontResolvingOptions(XFontStyle fontStyle) + { + FontStyle = fontStyle; + } + + public FontResolvingOptions(XFontStyle fontStyle, XStyleSimulations styleSimulations) + { + FontStyle = fontStyle; + OverrideStyleSimulations = true; + StyleSimulations = styleSimulations; + } + + public bool IsBold + { + get { return (FontStyle & XFontStyle.Bold) == XFontStyle.Bold; } + } + + public bool IsItalic + { + get { return (FontStyle & XFontStyle.Italic) == XFontStyle.Italic; } + } + + public bool IsBoldItalic + { + get { return (FontStyle & XFontStyle.BoldItalic) == XFontStyle.BoldItalic; } + } + + public bool MustSimulateBold + { + get { return (StyleSimulations & XStyleSimulations.BoldSimulation) == XStyleSimulations.BoldSimulation; } + } + + public bool MustSimulateItalic + { + get { return (StyleSimulations & XStyleSimulations.ItalicSimulation) == XStyleSimulations.ItalicSimulation; } + } + + public XFontStyle FontStyle; + + public bool OverrideStyleSimulations; + + public XStyleSimulations StyleSimulations; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/FontWriter.cs b/src/PDFsharp/src/PdfSharp/Fonts/FontWriter.cs new file mode 100644 index 00000000..a6b4cc36 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/FontWriter.cs @@ -0,0 +1,176 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.IO; + +namespace PdfSharp.Fonts +{ + /// + /// Represents a writer for generation of font file streams. + /// + internal class FontWriter + { + /// + /// Initializes a new instance of the class. + /// Data is written in Motorola format (big-endian). + /// + public FontWriter(Stream stream) + { + _stream = stream; + } + + /// + /// Closes the writer and, if specified, the underlying stream. + /// + public void Close(bool closeUnderlyingStream) + { + if (_stream != null && closeUnderlyingStream) + { +#if !UWP + _stream.Close(); +#endif + _stream.Dispose(); + } + _stream = null; + } + + /// + /// Closes the writer and the underlying stream. + /// + public void Close() + { + Close(true); + } + + /// + /// Gets or sets the position within the stream. + /// + public int Position + { + get { return (int)_stream.Position; } + set { _stream.Position = value; } + } + + /// + /// Writes the specified value to the font stream. + /// + public void WriteByte(byte value) + { + _stream.WriteByte(value); + } + + /// + /// Writes the specified value to the font stream. + /// + public void WriteByte(int value) + { + _stream.WriteByte((byte)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteShort(short value) + { + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteShort(int value) + { + WriteShort((short)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteUShort(ushort value) + { + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteUShort(int value) + { + WriteUShort((ushort)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteInt(int value) + { + _stream.WriteByte((byte)(value >> 24)); + _stream.WriteByte((byte)(value >> 16)); + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)value); + } + + /// + /// Writes the specified value to the font stream using big-endian. + /// + public void WriteUInt(uint value) + { + _stream.WriteByte((byte)(value >> 24)); + _stream.WriteByte((byte)(value >> 16)); + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)value); + } + + //public short ReadFWord() + //public ushort ReadUFWord() + //public long ReadLongDate() + //public string ReadString(int size) + + public void Write(byte[] buffer) + { + _stream.Write(buffer, 0, buffer.Length); + } + + public void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + } + + /// + /// Gets the underlying stream. + /// + internal Stream Stream + { + get { return _stream; } + } + Stream _stream; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/GlobalFontSettings.cs b/src/PDFsharp/src/PdfSharp/Fonts/GlobalFontSettings.cs new file mode 100644 index 00000000..d0cf7806 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/GlobalFontSettings.cs @@ -0,0 +1,116 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Internal; +using PdfSharp.Pdf; + +namespace PdfSharp.Fonts +{ + /// + /// Provides functionality to specify information about the handling of fonts in the current application domain. + /// + public static class GlobalFontSettings + { + /// + /// The name of the default font. + /// + public const string DefaultFontName = "PlatformDefault"; + + /// + /// Gets or sets the global font resolver for the current application domain. + /// This static function must be called only once and before any font operation was executed by PDFsharp. + /// If this is not easily to obtain, e.g. because your code is running on a web server, you must provide the + /// same instance of your font resolver in every subsequent setting of this property. + /// In a web application set the font resolver in Global.asax. + /// + public static IFontResolver FontResolver + { + get { return _fontResolver; } + set + { + // Cannot remove font resolver. + if (value == null) + throw new ArgumentNullException(); + + try + { + Lock.EnterFontFactory(); + // Ignore multiple setting e.g. in a web application. + if (ReferenceEquals(_fontResolver, value)) + return; + + if (FontFactory.HasFontSources) + throw new InvalidOperationException("Must not change font resolver after is was once used."); + + _fontResolver = value; + } + finally { Lock.ExitFontFactory(); } + } + } + static IFontResolver _fontResolver; + + /// + /// Gets or sets the default font encoding used for XFont objects where encoding is not explicitly specified. + /// If it is not set, the default value is PdfFontEncoding.Unicode. + /// If you are sure your document contains only Windows-1252 characters (see https://en.wikipedia.org/wiki/Windows-1252) + /// set default encoding to PdfFontEncodingj.Windows1252. + /// Must be set only once per app domain. + /// + public static PdfFontEncoding DefaultFontEncoding + { + get + { + if (!_fontEncodingInitialized) + DefaultFontEncoding = PdfFontEncoding.Unicode; + return _fontEncoding; + } + set + { + try + { + Lock.EnterFontFactory(); + if (_fontEncodingInitialized) + { + // Ignore multiple setting e.g. in a web application. + if (_fontEncoding == value) + return; + throw new InvalidOperationException("Must not change DefaultFontEncoding after is was set once."); + } + + _fontEncoding = value; + _fontEncodingInitialized = true; + } + finally { Lock.ExitFontFactory(); } + } + } + static PdfFontEncoding _fontEncoding; + static bool _fontEncodingInitialized; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts/IFontResolver.cs b/src/PDFsharp/src/PdfSharp/Fonts/IFontResolver.cs new file mode 100644 index 00000000..8dfa5233 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/IFontResolver.cs @@ -0,0 +1,54 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Fonts +{ + /// + /// Provides functionality that converts a requested typeface into a physical font. + /// + public interface IFontResolver + { + /// + /// Converts specified information about a required typeface into a specific font. + /// + /// Name of the font family. + /// Set to true when a bold fontface is required. + /// Set to true when an italic fontface is required. + /// Information about the physical font, or null if the request cannot be satisfied. + FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic); + + //FontResolverInfo ResolveTypeface(Typeface); TODO in PDFsharp 2.0 + + /// + /// Gets the bytes of a physical font with specified face name. + /// + /// A face name previously retrieved by ResolveTypeface. + byte[] GetFont(string faceName); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolver.cs b/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolver.cs new file mode 100644 index 00000000..f630d6c3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolver.cs @@ -0,0 +1,337 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if CORE || GDI +using System.Drawing; +using System.Drawing.Drawing2D; +using GdiFontFamily = System.Drawing.FontFamily; +using GdiFont = System.Drawing.Font; +using GdiFontStyle = System.Drawing.FontStyle; +#endif +#if WPF +using System.Windows; +using System.Windows.Documents; +using System.Windows.Media; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +using WpfStyleSimulations = System.Windows.Media.StyleSimulations; +#endif +using PdfSharp.Drawing; + +#pragma warning disable 1591 +// ReSharper disable RedundantNameQualifier + +namespace PdfSharp.Fonts +{ + /// + /// Default platform specific font resolving. + /// + public static class PlatformFontResolver + { + /// + /// Resolves the typeface by generating a font resolver info. + /// + /// Name of the font family. + /// Indicates whether a bold font is requested. + /// Indicates whether an italic font is requested. + public static FontResolverInfo ResolveTypeface(string familyName, bool isBold, bool isItalic) + { + FontResolvingOptions fontResolvingOptions = new FontResolvingOptions(FontHelper.CreateStyle(isBold, isItalic)); + return ResolveTypeface(familyName, fontResolvingOptions, XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions)); + } + + /// + /// Internal implementation. + /// + internal static FontResolverInfo ResolveTypeface(string familyName, FontResolvingOptions fontResolvingOptions, string typefaceKey) + { + // Internally we often have the typeface key already. + if (string.IsNullOrEmpty(typefaceKey)) + typefaceKey = XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions); + + // The user may call ResolveTypeface anytime from anywhere, so check cache in FontFactory in the first place. + FontResolverInfo fontResolverInfo; + if (FontFactory.TryGetFontResolverInfoByTypefaceKey(typefaceKey, out fontResolverInfo)) + return fontResolverInfo; + + // Let the platform create the requested font source and save both PlattformResolverInfo + // and XFontSource in FontFactory cache. + // It is possible that we already have the correct font source. E.g. we already have the regular typeface in cache + // and looking now for the italic typeface, but no such font exists. In this case we get the regular font source + // and cache again it with the italic typeface key. Furthermore in glyph typeface style simulation for italic is set. +#if (CORE || GDI) && !WPF + GdiFont gdiFont; + XFontSource fontSource = CreateFontSource(familyName, fontResolvingOptions, out gdiFont, typefaceKey); +#endif +#if WPF && !SILVERLIGHT + WpfFontFamily wpfFontFamily; + WpfTypeface wpfTypeface; + WpfGlyphTypeface wpfGlyphTypeface; + XFontSource fontSource = CreateFontSource(familyName, fontResolvingOptions, out wpfFontFamily, out wpfTypeface, out wpfGlyphTypeface, typefaceKey); +#endif +#if SILVERLIGHT + //GlyphTypeface wpfGlyphTypeface; + XFontSource fontSource = null;//CreateFontSource(familyName, isBold, isItalic, out wpfGlyphTypeface, typefaceKey); +#endif +#if NETFX_CORE || UWP || DNC10 + //GlyphTypeface wpfGlyphTypeface; + XFontSource fontSource = null;//CreateFontSource(familyName, isBold, isItalic, out wpfGlyphTypeface, typefaceKey); +#endif + // If no such font exists return null. PDFsharp will fail. + if (fontSource == null) + return null; + + //#if (CORE || GDI) && !WPF + // // TODO: Support style simulation for GDI+ platform fonts. + // fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, false, false, gdiFont); + //#endif + if (fontResolvingOptions.OverrideStyleSimulations) + { +#if (CORE || GDI) && !WPF + // TODO: Support style simulation for GDI+ platform fonts. + fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, gdiFont); +#endif +#if WPF && !SILVERLIGHT + fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, fontResolvingOptions.MustSimulateBold, fontResolvingOptions.MustSimulateItalic, + wpfFontFamily, wpfTypeface, wpfGlyphTypeface); +#endif + } + else + { +#if (CORE || GDI) && !WPF + bool mustSimulateBold = gdiFont.Bold && !fontSource.Fontface.os2.IsBold; + bool mustSimulateItalic = gdiFont.Italic && !fontSource.Fontface.os2.IsItalic; + fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, mustSimulateBold, mustSimulateItalic, gdiFont); +#endif +#if WPF && !SILVERLIGHT + // WPF knows what styles have to be simulated. + bool mustSimulateBold = (wpfGlyphTypeface.StyleSimulations & WpfStyleSimulations.BoldSimulation) == WpfStyleSimulations.BoldSimulation; + bool mustSimulateItalic = (wpfGlyphTypeface.StyleSimulations & WpfStyleSimulations.ItalicSimulation) == WpfStyleSimulations.ItalicSimulation; + + // Weird behavior of WPF is fixed here in case we request a bold italic typeface. + // If only italic is available, bold is simulated based on italic. + // If only bold is available, italic is simulated based on bold. + // But if both bold and italic is available, italic face is used and bold is simulated. + // The latter case is reversed here, i.e. bold face is used and italic is simulated. + if (fontResolvingOptions.IsBoldItalic && mustSimulateBold && !mustSimulateItalic) + { + // Try to get the bold typeface. + string typefaceKeyBold = XGlyphTypeface.ComputeKey(familyName, true, false); + FontResolverInfo infoBold = ResolveTypeface(familyName, + new FontResolvingOptions(FontHelper.CreateStyle(true, false)), typefaceKeyBold); + // Use it if it does not base on simulation. + if (infoBold != null && infoBold.StyleSimulations == XStyleSimulations.None) + { + // Use existing bold typeface and simulate italic. + fontResolverInfo = new PlatformFontResolverInfo(typefaceKeyBold, false, true, + wpfFontFamily, wpfTypeface, wpfGlyphTypeface); + } + else + { + // Simulate both. + fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, true, true, + wpfFontFamily, wpfTypeface, wpfGlyphTypeface); + } + } + else + { + fontResolverInfo = new PlatformFontResolverInfo(typefaceKey, mustSimulateBold, mustSimulateItalic, + wpfFontFamily, wpfTypeface, wpfGlyphTypeface); + } +#endif + } + +#if SILVERLIGHT + fontResolverInfo = null; //new PlattformResolverInfo(typefaceKey, false, false, wpfGlyphTypeface); +#endif + FontFactory.CacheFontResolverInfo(typefaceKey, fontResolverInfo); + + // Register font data under the platform specific face name. + // Already done in CreateFontSource. + // FontFactory.CacheNewFontSource(typefaceKey, fontSource); + + return fontResolverInfo; + } + +#if (CORE_WITH_GDI || GDI) && !WPF + /// + /// Create a GDI+ font and use its handle to retrieve font data using native calls. + /// + internal static XFontSource CreateFontSource(string familyName, FontResolvingOptions fontResolvingOptions, out GdiFont font, string typefaceKey) + { + if (string.IsNullOrEmpty(typefaceKey)) + typefaceKey = XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions); +#if true_ + if (familyName == "Cambria") + Debug-Break.Break(); +#endif + GdiFontStyle gdiStyle = (GdiFontStyle)(fontResolvingOptions.FontStyle & XFontStyle.BoldItalic); + + // Create a 10 point GDI+ font as an exemplar. + XFontSource fontSource; + font = FontHelper.CreateFont(familyName, 10, gdiStyle, out fontSource); + + if (fontSource != null) + { + Debug.Assert(font != null); + // Case: Font was created by a GDI+ private font collection. +#if true +#if DEBUG + XFontSource existingFontSource; + Debug.Assert(FontFactory.TryGetFontSourceByTypefaceKey(typefaceKey, out existingFontSource) && + ReferenceEquals(fontSource, existingFontSource)); +#endif +#else + // Win32 API cannot get font data from fonts created by private font collection, + // because this is handled internally in GDI+. + // Therefore the font source was created when the private font is added to the private font collection. + if (!FontFactory.TryGetFontSourceByTypefaceKey(typefaceKey, out fontSource)) + { + // Simplify styles. + // (The code is written for clarity - do not rearrange for optimization) + if (font.Bold && font.Italic) + { + if (FontFactory.TryGetFontSourceByTypefaceKey(XGlyphTypeface.ComputeKey(font.Name, true, false), out fontSource)) + { + // Use bold font. + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + else if (FontFactory.TryGetFontSourceByTypefaceKey(XGlyphTypeface.ComputeKey(font.Name, false, true), out fontSource)) + { + // Use italic font. + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + else if (FontFactory.TryGetFontSourceByTypefaceKey(XGlyphTypeface.ComputeKey(font.Name, false, false), out fontSource)) + { + // Use regular font. + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + } + else if (font.Bold || font.Italic) + { + // Use regular font. + if (FontFactory.TryGetFontSourceByTypefaceKey(XGlyphTypeface.ComputeKey(font.Name, false, false), out fontSource)) + { + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + } + else + { + if (FontFactory.TryGetFontSourceByTypefaceKey(XGlyphTypeface.ComputeKey(font.Name, false, false), out fontSource)) + { + // Should never come here... + FontFactory.CacheExistingFontSourceWithNewTypefaceKey(typefaceKey, fontSource); + } + } + } +#endif + } + else + { + // Get or create the font source and cache it under the specified typeface key. + fontSource = XFontSource.GetOrCreateFromGdi(typefaceKey, font); + } + return fontSource; + } +#endif + +#if WPF && !SILVERLIGHT + /// + /// Create a WPF GlyphTypeface and retrieve font data from it. + /// + internal static XFontSource CreateFontSource(string familyName, FontResolvingOptions fontResolvingOptions, + out WpfFontFamily wpfFontFamily, out WpfTypeface wpfTypeface, out WpfGlyphTypeface wpfGlyphTypeface, string typefaceKey) + { + if (string.IsNullOrEmpty(typefaceKey)) + typefaceKey = XGlyphTypeface.ComputeKey(familyName, fontResolvingOptions); + XFontStyle style = fontResolvingOptions.FontStyle; + +#if DEBUG + if (StringComparer.OrdinalIgnoreCase.Compare(familyName, "Segoe UI Semilight") == 0 + && (style & XFontStyle.BoldItalic) == XFontStyle.Italic) + familyName.GetType(); +#endif + + // Use WPF technique to create font data. + wpfTypeface = XPrivateFontCollection.TryCreateTypeface(familyName, style, out wpfFontFamily); +#if DEBUG__ + if (wpfTypeface != null) + { + WpfGlyphTypeface glyphTypeface; + ICollection list = wpfFontFamily.GetTypefaces(); + foreach (WpfTypeface tf in list) + { + if (!tf.TryGetGlyphTypeface(out glyphTypeface)) + Debug-Break.Break(); + } + + //if (!WpfTypeface.TryGetGlyphTypeface(out glyphTypeface)) + // throw new InvalidOperationException(PSSR.CannotGetGlyphTypeface(familyName)); + } +#endif + if (wpfFontFamily == null) + wpfFontFamily = new WpfFontFamily(familyName); + + if (wpfTypeface == null) + wpfTypeface = FontHelper.CreateTypeface(wpfFontFamily, style); + + // Let WPF choose the right glyph typeface. + if (!wpfTypeface.TryGetGlyphTypeface(out wpfGlyphTypeface)) + throw new InvalidOperationException(PSSR.CannotGetGlyphTypeface(familyName)); + + // Get or create the font source and cache it under the specified typeface key. + XFontSource fontSource = XFontSource.GetOrCreateFromWpf(typefaceKey, wpfGlyphTypeface); + return fontSource; + } +#endif + +#if SILVERLIGHT + /// + /// Silverlight has no access to the bytes of its fonts and therefore return null. + /// + internal static XFontSource CreateFontSource(string familyName, bool isBold, bool isItalic) + { + // PDFsharp does not provide a default font because this would blow up the assembly + // unnecessarily if the font is not needed. Provide your own font resolver to generate + // PDF files containing text. + return null; + } +#endif + +#if NETFX_CORE + internal static XFontSource CreateFontSource(string familyName, bool isBold, bool isItalic, string typefaceKey) + { + throw new NotImplementedException(); + } +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolverInfo.cs b/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolverInfo.cs new file mode 100644 index 00000000..ba4a8994 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Fonts/PlatformFontResolverInfo.cs @@ -0,0 +1,101 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if CORE || GDI +using System.Drawing; +using GdiFont = System.Drawing.Font; + +#endif +#if WPF +using System.Windows.Media; +using WpfFontFamily = System.Windows.Media.FontFamily; +using WpfTypeface = System.Windows.Media.Typeface; +using WpfGlyphTypeface = System.Windows.Media.GlyphTypeface; +#endif + +namespace PdfSharp.Fonts +{ + /// + /// Represents a font resolver info created by the platform font resolver. + /// + internal class PlatformFontResolverInfo : FontResolverInfo + { +#if CORE || GDI + public PlatformFontResolverInfo(string faceName, bool mustSimulateBold, bool mustSimulateItalic, GdiFont gdiFont) + : base(faceName, mustSimulateBold, mustSimulateItalic) + { + _gdiFont = gdiFont; + } +#endif +#if WPF + public PlatformFontResolverInfo(string faceName, bool mustSimulateBold, bool mustSimulateItalic, WpfFontFamily wpfFontFamily, + WpfTypeface wpfTypeface, WpfGlyphTypeface wpfGlyphTypeface) + : base(faceName, mustSimulateBold, mustSimulateItalic) + { + _wpfFontFamily = wpfFontFamily; + _wpfTypeface = wpfTypeface; + _wpfGlyphTypeface = wpfGlyphTypeface; + } +#endif + +#if CORE || GDI + public Font GdiFont + { + get { return _gdiFont; } + } + readonly Font _gdiFont; +#endif +#if WPF + public WpfFontFamily WpfFontFamily + { + get { return _wpfFontFamily; } + } + readonly WpfFontFamily _wpfFontFamily; + + public WpfTypeface WpfTypeface + { + get { return _wpfTypeface; } + } + readonly WpfTypeface _wpfTypeface; + + public WpfGlyphTypeface WpfGlyphTypeface + { + get { return _wpfGlyphTypeface; } + } + readonly WpfGlyphTypeface _wpfGlyphTypeface; +#endif +#if NETFX_CORE || UWP || DNC10 + public PlatformFontResolverInfo(string faceName, bool mustSimulateBold, bool mustSimulateItalic) + : base(faceName, mustSimulateBold, mustSimulateItalic) + { + //_gdiFont = gdiFont; + } +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Forms/ColorComboBox.cs b/src/PDFsharp/src/PdfSharp/Forms/ColorComboBox.cs new file mode 100644 index 00000000..b8da54dc --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/ColorComboBox.cs @@ -0,0 +1,202 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Text; +#if GDI +using System.Drawing; +using System.Windows.Forms; +#endif +using PdfSharp.Drawing; + +#if GDI +namespace PdfSharp.Forms +{ + /// + /// A combo box control for selection XColor values. + /// + public class ColorComboBox : ComboBox + { + /// + /// Initializes a new instance of the class. + /// + public ColorComboBox() + { + DropDownStyle = ComboBoxStyle.DropDownList; + DrawMode = DrawMode.OwnerDrawFixed; + Fill(); + } + + readonly XColorResourceManager _crm = new XColorResourceManager(); + + /// + /// Gets or sets the custom color. + /// + public XColor Color + { + get { return _color; } + set + { + _color = value; + if (value.IsKnownColor) + { + XKnownColor color = XColorResourceManager.GetKnownColor(value.Argb); + for (int idx = 1; idx < Items.Count; idx++) + { + if (((ColorItem)Items[idx]).Color.Argb == value.Argb) + { + SelectedIndex = idx; + break; + } + } + } + else + SelectedIndex = 0; + Invalidate(); + } + } + XColor _color = XColor.Empty; + + void Fill() + { + Items.Add(new ColorItem(XColor.Empty, "custom")); + XKnownColor[] knownColors = XColorResourceManager.GetKnownColors(false); + int count = knownColors.Length; + for (int idx = 0; idx < knownColors.Length; idx++) + { + XKnownColor color = knownColors[idx]; + Items.Add(new ColorItem(XColor.FromKnownColor(color), _crm.ToColorName(color))); + } + } + + /// + /// Keep control a drop down combo box. + /// + protected override void OnDropDownStyleChanged(EventArgs e) + { + DropDownStyle = ComboBoxStyle.DropDownList; + base.OnDropDownStyleChanged(e); + } + + /// + /// Sets the color with the selected item. + /// + protected override void OnSelectedIndexChanged(EventArgs e) + { + int index = SelectedIndex; + if (index > 0) + { + ColorItem item = (ColorItem)Items[index]; + _color = item.Color; + } + base.OnSelectedIndexChanged(e); + } + + /// + /// Draw a color entry. + /// + protected override void OnDrawItem(DrawItemEventArgs e) + { + int idx = e.Index; + + // Nothing selected? + if (idx < 0) + return; + + object obj = Items[idx]; + if (obj is ColorItem) + { + ColorItem item = (ColorItem)obj; + + // Is custom color? + if (idx == 0) + { + string name; + if (_color.IsEmpty) + name = "custom"; + else + name = _crm.ToColorName(_color); + + item = new ColorItem(_color, name); + } + + XColor clr = item.Color; + Graphics gfx = e.Graphics; + Rectangle rect = e.Bounds; + Brush textbrush = SystemBrushes.ControlText; + if ((e.State & DrawItemState.Selected) == 0) + { + gfx.FillRectangle(SystemBrushes.Window, rect); + textbrush = SystemBrushes.ControlText; + } + else + { + gfx.FillRectangle(SystemBrushes.Highlight, rect); + textbrush = SystemBrushes.HighlightText; + } + + // Draw color box + if (!clr.IsEmpty) + { + Rectangle box = new Rectangle(rect.X + 3, rect.Y + 1, rect.Height * 2, rect.Height - 3); + gfx.FillRectangle(new SolidBrush(clr.ToGdiColor()), box); + gfx.DrawRectangle(Pens.Black, box); + } + + StringFormat format = new StringFormat(StringFormat.GenericDefault); + format.Alignment = StringAlignment.Near; + format.LineAlignment = StringAlignment.Center; + rect.X += rect.Height * 2 + 3 + 3; + gfx.DrawString(item.Name, Font, textbrush, rect, format); + } + } + + /// + /// Represents a combo box item. + /// + struct ColorItem + { + public ColorItem(XColor color, string name) + { + Color = color; + Name = name; + } + + public override string ToString() + { + return Name; + } + + public XColor Color; + public string Name; + } + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/Forms/DeviceInfos.cs b/src/PDFsharp/src/PdfSharp/Forms/DeviceInfos.cs new file mode 100644 index 00000000..f7a0c47f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/DeviceInfos.cs @@ -0,0 +1,128 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Security; +//using System.Security.Permissions; + +#if GDI +namespace PdfSharp.Forms +{ + /// + /// Contains information about a physical device like a display or a printer. + /// + public struct DeviceInfos + { + /// + /// Width, in millimeters, of the physical screen or device. + /// + public int HorizontalSize; + + /// + /// Height, in millimeters, of the physical screen or device. + /// + public int VerticalSize; + + /// + /// Width, in pixels, of the screen or device. + /// + public int HorizontalResolution; + + /// + /// Height, in pixels, of the screen or device. + /// + public int VerticalResolution; + + /// + /// Number of pixels per logical inch along the screen or device width. + /// + public int LogicalDpiX; + + /// + /// Number of pixels per logical inch along the screen or device height. + /// + public int LogicalDpiY; + + /// + /// Number of pixels per physical inch along the screen or device width. + /// + public float PhysicalDpiX; + + /// + /// Number of pixels per physical inch along the screen or device height. + /// + public float PhysicalDpiY; + + /// + /// The ratio of LogicalDpiX and PhysicalDpiX. + /// + public float ScaleX; + + /// + /// The ratio of LogicalDpiY and PhysicalDpiY. + /// + public float ScaleY; + + /// + /// Gets a DeviceInfo for the specifed device context. + /// + [SuppressUnmanagedCodeSecurity] + public static DeviceInfos GetInfos(IntPtr hdc) + { + DeviceInfos devInfo; + + devInfo.HorizontalSize = GetDeviceCaps(hdc, HORZSIZE); + devInfo.VerticalSize = GetDeviceCaps(hdc, VERTSIZE); + devInfo.HorizontalResolution = GetDeviceCaps(hdc, HORZRES); + devInfo.VerticalResolution = GetDeviceCaps(hdc, VERTRES); + devInfo.LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX); + devInfo.LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY); + devInfo.PhysicalDpiX = devInfo.HorizontalResolution * 25.4f / devInfo.HorizontalSize; + devInfo.PhysicalDpiY = devInfo.VerticalResolution * 25.4f / devInfo.VerticalSize; + devInfo.ScaleX = devInfo.LogicalDpiX / devInfo.PhysicalDpiX; + devInfo.ScaleY = devInfo.LogicalDpiY / devInfo.PhysicalDpiY; + + return devInfo; + } + + [DllImport("gdi32.dll")] + static extern int GetDeviceCaps(IntPtr hdc, int capability); + // ReSharper disable InconsistentNaming + const int HORZSIZE = 4; + const int VERTSIZE = 6; + const int HORZRES = 8; + const int VERTRES = 10; + const int LOGPIXELSX = 88; + const int LOGPIXELSY = 90; + // ReSharper restore InconsistentNaming + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/Forms/PagePreview.cs b/src/PDFsharp/src/PdfSharp/Forms/PagePreview.cs new file mode 100644 index 00000000..ae871797 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/PagePreview.cs @@ -0,0 +1,1077 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Draw crosses to check layout calculation +#define DRAW_X_ + +#if DEBUG +// Test drawing in a bitmap. This is just a hack - don't use it! +#define DRAW_BMP_ +#endif + +using System; +using System.Diagnostics; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using PdfSharp.Drawing; + +#if !GDI +#error This file must only be included in GDI build. +#endif + +namespace PdfSharp.Forms +{ + /* TODOs + * + * o Call render event only once. -> introduce an UpdatePage() function + * + * Further stuff: set printable area; set text box (smallest rect that contains all content) + */ + /// + /// Represents a preview control for an XGraphics page. Can be used as an alternative to + /// System.Windows.Forms.PrintPreviewControl. + /// + public class PagePreview : UserControl + { + /// + /// A delegate for invoking the render function. + /// + public delegate void RenderEvent(XGraphics gfx); + + private Container components = null; + + /// + /// Initializes a new instance of the class. + /// + public PagePreview() + { + _canvas = new PagePreviewCanvas(this); + Controls.Add(_canvas); + + _hScrollBar = new HScrollBar(); + _hScrollBar.Visible = _showScrollbars; + _hScrollBar.Scroll += OnScroll; + _hScrollBar.ValueChanged += OnValueChanged; + Controls.Add(_hScrollBar); + + _vScrollBar = new VScrollBar(); + _vScrollBar.Visible = _showScrollbars; + _vScrollBar.Scroll += OnScroll; + _vScrollBar.ValueChanged += OnValueChanged; + Controls.Add(_vScrollBar); + + InitializeComponent(); + //OnLayout(); + + _zoom = Zoom.FullPage; + _printableArea = new RectangleF(); + //virtPageSize = new Size(); + //showNonPrintableArea = false; + //virtualPrintableArea = new Rectangle(); + + _printableArea.GetType(); + //showNonPrintableArea.GetType(); + //virtualPrintableArea.GetType(); + + // Prevent bogus compiler warnings + _posOffset = new Point(); + _virtualPage = new Rectangle(); + } + + readonly PagePreviewCanvas _canvas; + readonly HScrollBar _hScrollBar; + readonly VScrollBar _vScrollBar; + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + components.Dispose(); + } + base.Dispose(disposing); + } + + /// + /// Gets or sets the border style of the control. + /// + /// + [DefaultValue((int)BorderStyle.Fixed3D), Description("Determines the style of the border."), Category("Preview Properties")] + public new BorderStyle BorderStyle + { + get { return _borderStyle; } + set + { + if (!Enum.IsDefined(typeof(BorderStyle), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); + + if (value != _borderStyle) + { + _borderStyle = value; + LayoutChildren(); + } + } + } + BorderStyle _borderStyle = BorderStyle.Fixed3D; + + // [DefaultValue(2), Description("TODO..."), Category("Preview Properties")] + // public PageSize PageSize + // { + // get { return pageSize2; } + // set + // { + // if (!Enum.IsDefined(typeof(PageSize), value)) + // throw new InvalidEnumArgumentException("value", (int)value, typeof(PageSize)); + // + // if (value != pageSize2) + // { + // pageSize2 = value; + // // base.RecreateHandle(); + // // integralHeightAdjust = true; + // // try + // // { + // // base.Height = requestedHeight; + // // } + // // finally + // // { + // // integralHeightAdjust = false; + // // } + // } + // } + // } + // PageSize pageSize2; + + /// + /// Gets or sets the XGraphicsUnit of the page. + /// The default value is XGraphicsUnit.Point. + /// + public XGraphicsUnit PageGraphicsUnit + { + get { return _pageGraphicsUnit; } + set { _pageGraphicsUnit = value; } + } + XGraphicsUnit _pageGraphicsUnit = XGraphicsUnit.Point; + + /// + /// This property was renamed. Use new property PageGraphicsUnit. + /// + [Obsolete("Property renamed, use PageGraphicsUnit")] + public XGraphicsUnit PageUnit + { + get { return _pageGraphicsUnit; } + set { _pageGraphicsUnit = value; } + } + + /// + /// Gets or sets a predefined zoom factor. + /// + [DefaultValue((int)Zoom.FullPage), Description("Determines the zoom of the page."), Category("Preview Properties")] + public Zoom Zoom + { + get { return _zoom; } + set + { + if ((int)value < (int)Zoom.Mininum || (int)value > (int)Zoom.Maximum) + { + if (!Enum.IsDefined(typeof(Zoom), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(Zoom)); + } + if (value != _zoom) + { + _zoom = value; + CalculatePreviewDimension(); + SetScrollBarRange(); + _canvas.Invalidate(); + } + } + } + Zoom _zoom; + + /// + /// Gets or sets an arbitrary zoom factor. The range is from 10 to 800. + /// + //[DefaultValue((int)Zoom.FullPage), Description("Determines the zoom of the page."), Category("Preview Properties")] + public int ZoomPercent + { + get { return _zoomPercent; } + set + { + if (value < (int)Zoom.Mininum || value > (int)Zoom.Maximum) + throw new ArgumentOutOfRangeException("value", value, + String.Format("Value must between {0} and {1}.", (int)Zoom.Mininum, (int)Zoom.Maximum)); + + if (value != _zoomPercent) + { + _zoom = (Zoom)value; + _zoomPercent = value; + CalculatePreviewDimension(); + SetScrollBarRange(); + _canvas.Invalidate(); + } + } + } + int _zoomPercent; + + /// + /// Gets or sets the color of the page. + /// + [Description("The background color of the page."), Category("Preview Properties")] + public Color PageColor + { + get { return _pageColor; } + set + { + if (value != _pageColor) + { + _pageColor = value; + Invalidate(); + } + } + } + Color _pageColor = Color.GhostWhite; + + /// + /// Gets or sets the color of the desktop. + /// + [Description("The color of the desktop."), Category("Preview Properties")] + public Color DesktopColor + { + get { return _desktopColor; } + set + { + if (value != _desktopColor) + { + _desktopColor = value; + Invalidate(); + } + } + } + internal Color _desktopColor = SystemColors.ControlDark; + + /// + /// Gets or sets a value indicating whether the scrollbars are visible. + /// + [DefaultValue(true), Description("Determines whether the scrollbars are visible."), Category("Preview Properties")] + public bool ShowScrollbars + { + get { return _showScrollbars; } + set + { + if (value != _showScrollbars) + { + _showScrollbars = value; + _hScrollBar.Visible = value; + _vScrollBar.Visible = value; + LayoutChildren(); + } + } + } + bool _showScrollbars = true; + + /// + /// Gets or sets a value indicating whether the page is visible. + /// + [DefaultValue(true), Description("Determines whether the page visible."), Category("Preview Properties")] + public bool ShowPage + { + get { return _showPage; } + set + { + if (value != _showPage) + { + _showPage = value; + _canvas.Invalidate(); + } + } + } + internal bool _showPage = true; + + /// + /// Gets or sets the page size in point. + /// + [Description("Determines the size (in points) of the page."), Category("Preview Properties")] + public XSize PageSize + { + get { return new XSize((int)_pageSize.Width, (int)_pageSize.Height); } + set + { + _pageSize = new SizeF((float)value.Width, (float)value.Height); + CalculatePreviewDimension(); + Invalidate(); + } + } + + /// + /// This is a hack for Visual Studio 2008. The designer uses reflection for setting the PageSize property. + /// This fails, even an implicit operator that converts Size to XSize exits. + /// + public Size PageSizeF + { + get { return new Size(Convert.ToInt32(_pageSize.Width), Convert.ToInt32(_pageSize.Height)); } + set + { + _pageSize = value; + CalculatePreviewDimension(); + Invalidate(); + } + } + + /// + /// Sets a delegate that is invoked when the preview wants to be painted. + /// + public void SetRenderFunction(Action renderEvent) + { + _renderAction = renderEvent; + Invalidate(); + } + Action _renderAction; + + /// + /// Sets a delegate that is invoked when the preview wants to be painted. + /// + [Obsolete("Use SetRenderFunction")] + public void SetRenderEvent(RenderEvent renderEvent) + { + _renderAction = new Action(renderEvent); + Invalidate(); + } + + #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() + { + Name = "PagePreview"; + Size = new System.Drawing.Size(228, 252); + } + #endregion + + /// + /// Raises the ZoomChanged event when the zoom factor changed. + /// + protected virtual void OnZoomChanged(EventArgs e) + { + if (ZoomChanged != null) + ZoomChanged(this, e); + } + + /// + /// Occurs when the zoom factor changed. + /// + public event EventHandler ZoomChanged; + + /// + /// Paints the background with the sheet of paper. + /// + protected override void OnPaintBackground(PaintEventArgs e) + { + // Accurate drawing prevents flickering + Graphics gfx = e.Graphics; + Rectangle clientRect = ClientRectangle; + int d = 0; + switch (_borderStyle) + { + case BorderStyle.FixedSingle: + gfx.DrawRectangle(SystemPens.WindowFrame, clientRect.X, clientRect.Y, clientRect.Width - 1, clientRect.Height - 1); + d = 1; + break; + + case BorderStyle.Fixed3D: + ControlPaint.DrawBorder3D(gfx, clientRect, Border3DStyle.Sunken); + d = 2; + break; + } + if (_showScrollbars) + { + int cxScrollbar = SystemInformation.VerticalScrollBarWidth; + int cyScrollbar = SystemInformation.HorizontalScrollBarHeight; + + gfx.FillRectangle(new SolidBrush(BackColor), + clientRect.Width - cxScrollbar - d, clientRect.Height - cyScrollbar - d, cxScrollbar, cyScrollbar); + } + } + + /// + /// Recalculates the preview dimension. + /// + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + CalculatePreviewDimension(); + SetScrollBarRange(); + } + + /// + /// Invalidates the canvas. + /// + protected override void OnInvalidated(InvalidateEventArgs e) + { + base.OnInvalidated(e); + _canvas.Invalidate(); + } + + /// + /// Layouts the child controls. + /// + protected override void OnLayout(LayoutEventArgs levent) + { + LayoutChildren(); + } + + void OnScroll(object obj, ScrollEventArgs e) + { + ScrollBar sc = obj as ScrollBar; + if (sc != null) + { + //Debug.WriteLine(String.Format("OnScroll: {0}, {1}", sc.Value, e.NewValue)); + } + } + + void OnValueChanged(object obj, EventArgs e) + { + ScrollBar sc = obj as ScrollBar; + if (sc != null) + { + //Debug.WriteLine(String.Format("OnValueChanged: {0}", sc.Value)); + if (sc == _hScrollBar) + _posOffset.X = sc.Value; + + else if (sc == _vScrollBar) + _posOffset.Y = sc.Value; + } + _canvas.Invalidate(); + } + + void LayoutChildren() + { + Invalidate(); + Rectangle clientRect = ClientRectangle; + switch (_borderStyle) + { + case BorderStyle.FixedSingle: + clientRect.Inflate(-1, -1); + break; + + case BorderStyle.Fixed3D: + clientRect.Inflate(-2, -2); + break; + } + int x = clientRect.X; + int y = clientRect.Y; + int cx = clientRect.Width; + int cy = clientRect.Height; + int cxScrollbar = 0; + int cyScrollbar = 0; + if (_showScrollbars && _vScrollBar != null && _hScrollBar != null) + { + cxScrollbar = _vScrollBar.Width; + cyScrollbar = _hScrollBar.Height; + _vScrollBar.Location = new Point(x + cx - cxScrollbar, y); + _vScrollBar.Size = new Size(cxScrollbar, cy - cyScrollbar); + _hScrollBar.Location = new Point(x, y + cy - cyScrollbar); + _hScrollBar.Size = new Size(cx - cxScrollbar, cyScrollbar); + } + if (_canvas != null) + { + _canvas.Location = new Point(x, y); + _canvas.Size = new Size(cx - cxScrollbar, cy - cyScrollbar); + } + } + + /// + /// Calculates all values for drawing the page preview. + /// + internal void CalculatePreviewDimension(out bool zoomChanged) + { + // User may change display resolution while preview is running + Graphics gfx = Graphics.FromHwnd(IntPtr.Zero); + IntPtr hdc = gfx.GetHdc(); + DeviceInfos devInfo = DeviceInfos.GetInfos(hdc); + gfx.ReleaseHdc(hdc); + gfx.Dispose(); + int xdpiScreen = devInfo.LogicalDpiX; + int ydpiScreen = devInfo.LogicalDpiY; + //int cxScrollbar = SystemInformation.VerticalScrollBarWidth; + //int cyScrollbar = SystemInformation.HorizontalScrollBarHeight; + Rectangle rcCanvas = _canvas.ClientRectangle; + + Zoom zoomOld = _zoom; + int zoomPercentOld = _zoomPercent; + + // Border around virtual page in pixel. + const int leftBorder = 2; + const int rightBorder = 4; // because of shadow + const int topBorder = 2; + const int bottomBorder = 4; // because of shadow + const int horzBorders = leftBorder + rightBorder; + const int vertBorders = topBorder + bottomBorder; + + // Calculate new zoom factor. + switch (_zoom) + { + case Zoom.BestFit: + BestFit: + //zoomPercent = Convert.ToInt32(25400.0 * (rcCanvas.Width - (leftBorder + rightBorder)) / (this.pageSize.Width * xdpiScreen)); + _zoomPercent = (int)(7200f * (rcCanvas.Width - horzBorders) / (_pageSize.Width * xdpiScreen)); + //--zoomPercent; // prevent round up errors + break; + + case Zoom.TextFit: + // TODO: 'public Rectangle TextBox' property + goto BestFit; + //zoomPercent = LongFromReal (25400.0 / (_cxUsedPage + 0) * + // (rcWnd.CX () - 2 * cxScrollbar) / xdpiScreen) - 3; + //break; + + case Zoom.FullPage: + { + //int zoomX = Convert.ToInt32(25400.0 / (pageSize.Width) * + // (rcCanvas.Width - (leftBorder + rightBorder)) / xdpiScreen); + //int zoomY = Convert.ToInt32(25400.0 / (pageSize.Height) * + // (rcCanvas.Height - (topBorder + bottomBorder)) / ydpiScreen); + int zoomX = (int)(7200f * (rcCanvas.Width - horzBorders) / (_pageSize.Width * xdpiScreen)); + int zoomY = (int)(7200f * (rcCanvas.Height - vertBorders) / (_pageSize.Height * ydpiScreen)); + _zoomPercent = Math.Min(zoomX, zoomY); + //--zoomPercent; // prevent round up errors + } + break; + + case Zoom.OriginalSize: + _zoomPercent = (int)(0.5 + 200f / (devInfo.ScaleX + devInfo.ScaleY)); + _zoomPercent = (int)(0.5 + 100f / devInfo.ScaleX); + break; + + default: + _zoomPercent = (int)_zoom; + break; + } + + // Bound to zoom limits + _zoomPercent = Math.Max(Math.Min(_zoomPercent, (int)Zoom.Maximum), (int)Zoom.Mininum); + if ((int)_zoom > 0) + _zoom = (Zoom)_zoomPercent; + + // Size of page in preview window in pixel + _virtualPage.X = leftBorder; + _virtualPage.Y = topBorder; + _virtualPage.Width = (int)(_pageSize.Width * xdpiScreen * _zoomPercent / 7200); + _virtualPage.Height = (int)(_pageSize.Height * ydpiScreen * _zoomPercent / 7200); + + //// 2540 := (25.4mm * 100%) / 1mm + //m_VirtualPrintableArea.X = (int)printableArea.X * this.zoomPercent * xdpiScreen / 2540; + //m_VirtualPrintableArea.Y = (int)printableArea.Y * this.zoomPercent * xdpiScreen / 2540; + //m_VirtualPrintableArea.Width = (int)printableArea.Width * this.zoomPercent * xdpiScreen / 2540; + //m_VirtualPrintableArea.Height = (int)printableArea.Height * this.zoomPercent * xdpiScreen / 2540; + + // Border do not depend on zoom anymore + _virtualCanvas = new Size(_virtualPage.Width + horzBorders, _virtualPage.Height + vertBorders); + + // Adjust virtual canvas to at least actual window size + if (_virtualCanvas.Width < rcCanvas.Width) + { + _virtualCanvas.Width = rcCanvas.Width; + _virtualPage.X = leftBorder + (rcCanvas.Width - horzBorders - _virtualPage.Width) / 2; + } + if (_virtualCanvas.Height < rcCanvas.Height) + { + _virtualCanvas.Height = rcCanvas.Height; + _virtualPage.Y = topBorder + (rcCanvas.Height - vertBorders - _virtualPage.Height) / 2; + } + + zoomChanged = zoomOld != _zoom || zoomPercentOld != _zoomPercent; + if (zoomChanged) + OnZoomChanged(new EventArgs()); + } + + internal void CalculatePreviewDimension() + { + bool zoomChanged; + CalculatePreviewDimension(out zoomChanged); + } + + internal bool RenderPage(Graphics gfx) + { + //delete m_RenderContext; + //m_RenderContext = new HdcRenderContext(wdc.m_hdc); + + gfx.TranslateTransform(-_posOffset.X, -_posOffset.Y); + gfx.SetClip(new Rectangle(_virtualPage.X + 1, _virtualPage.Y + 1, _virtualPage.Width - 1, _virtualPage.Height - 1)); + + float scaleX = _virtualPage.Width / _pageSize.Width; + float scaleY = _virtualPage.Height / _pageSize.Height; + + //gfx.SetSmoothingMode(SmoothingModeAntiAlias); + //PaintBackground(gfx); + +#if DRAW_BMP + Matrix matrix = new Matrix(); + matrix.Translate(virtualPage.X, virtualPage.Y); + matrix.Translate(-posOffset.X, -this.posOffset.Y); + //matrix.Scale(scaleX, scaleY); + gfx.Transform = matrix; + +#if DRAW_X + gfx.DrawLine(Pens.Red, 0, 0, pageSize.Width, pageSize.Height); + gfx.DrawLine(Pens.Red, 0, pageSize.Height, pageSize.Width, 0); +#endif + if (renderEvent != null) + { + Bitmap bmp = new Bitmap(virtualPage.Width, this.virtualPage.Height, gfx); + Graphics gfx2 = Graphics.FromImage(bmp); + gfx2.Clear(pageColor); + gfx2.ScaleTransform(scaleX, scaleY); + gfx2.SmoothingMode = SmoothingMode.HighQuality; + XGraphics xgfx = XGraphics.FromGraphics(gfx2, new XSize(pageSize.Width, this.pageSize.Height)); + try + { + renderEvent(xgfx); + gfx.DrawImage(bmp, 0, 0); + } + finally + { + bmp.Dispose(); + } + } +#else + Matrix matrix = new Matrix(); + matrix.Translate(_virtualPage.X, _virtualPage.Y); + matrix.Translate(-_posOffset.X, -_posOffset.Y); + matrix.Scale(scaleX, scaleY); + gfx.Transform = matrix; + +#if DRAW_X + gfx.DrawLine(Pens.Red, 0, 0, pageSize.Width, pageSize.Height); + gfx.DrawLine(Pens.Red, 0, pageSize.Height, pageSize.Width, 0); +#endif + + if (_renderAction != null) + { + gfx.SmoothingMode = SmoothingMode.HighQuality; + XGraphics xgfx = XGraphics.FromGraphics(gfx, new XSize(_pageSize.Width, _pageSize.Height), PageGraphicsUnit); + try + { + _renderAction(xgfx); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Exception"); + } + } +#endif + + // Old C++ stuff, may be useful later... +#if false + switch (m_mode) + { + case RenderModeDirect: + { + delete m_PreviewMetafile; + m_PreviewMetafile = NULL; + + float cxPage = Metric::MillimetersToPoints(m_dimPage.cx / 10.0f); + float cyPage = Metric::MillimetersToPoints(m_dimPage.cy / 10.0f); + + float scaleX = virtualPage.Width / cxPage; + float scaleY = virtualPage.Height / cyPage; + + Graphics gfx(m_RenderContext); + gfx.SetSmoothingMode(SmoothingModeAntiAlias); + PaintBackground(gfx, &virtualPage); + + Matrix matrix; + matrix.Translate((float)virtualPage.X, (float)virtualPage.Y); + matrix.Translate((float) - m_posOffset.x, (float) -m_posOffset.y); + matrix.Scale(scaleX, scaleY); + + m_RenderContext->SetDefaultViewMatrix(&matrix); + gfx.ResetTransform(); + if (m_PreviewRenderer && m_PreviewRenderer->CanRender()) + m_PreviewRenderer->Render(&gfx, m_Page); + } + break; + + case RenderModeMetafile: + { + Graphics gfx(m_RenderContext); + if (m_PreviewMetafile == NULL) + { + float cxPage = Metric::MillimetersToPoints(m_dimPage.cx / 10.0f); + float cyPage = Metric::MillimetersToPoints(m_dimPage.cy / 10.0f); + + //float factor = 72.0f / 96.0f; + Rect rcLogicalPage(0, 0, (int)cxPage, (int)cyPage); + RectF rcFLogicalPage(0, 0, cxPage, cyPage); + + //DeviceName devname; + //DESKTOP::QueryDefaultPrinter(devname); //HACK DRUCKER MUSS DA SEIN! + //DeviceMode devmode(devname); + + //HDC hdc = ::CreateIC(devname.m_szDriver, devname.m_szDevice, devname.m_szOutput, devmode.m_pdm); + + //HDC hdc = m_Graphics->GetHDC(); + //HDC hdc = ::GetDC(NULL); + HDC hdc = ::CreateIC("DISPLAY", NULL, NULL, NULL); + + + float dpiX = gfx.GetDpiX(); + float dpiY = gfx.GetDpiY(); + + // Even Petzold would be surprised about that... + // Display | LaserJet + // DPI 96 : 120 | 300 + // physical device size in MM --------------------------------------------- + int horzSizeMM = ::GetDeviceCaps(hdc, HORZSIZE); // = 330 : 254 | 198 (not 210) + int vertSizeMM = ::GetDeviceCaps(hdc, VERTSIZE); // = 254 : 203 | 288 (hot 297) + + // device size in pixel + int horzSizePixel = ::GetDeviceCaps(hdc, HORZRES); // = 1280 : 1280 | 4676 + int vertSizePixel = ::GetDeviceCaps(hdc, VERTRES); // = 1024 : 1024 | 6814 + + // 'logical' device resolution in DPI + int logResX = ::GetDeviceCaps(hdc, LOGPIXELSX); // = 96 : 120 | 600 + int logResY = ::GetDeviceCaps(hdc, LOGPIXELSY); // = 96 : 120 | 600 + + // physical pixel size in .01 MM units + // accidentally(?) the result of GetPhysicalDimension! + //float X1 = 100.0f * horzSizeMM / horzSizePixel; // = 25.781250 : 19.843750 | 4.2343884 + //float Y1 = 100.0f * vertSizeMM / vertSizePixel; // = 24.804688 : 19.824219 | 4.2265925 + + // now we can get the 'physical' device resolution... + float phyResX = horzSizePixel / (horzSizeMM / 25.4f); // = 98.521210 : 128.00000 | 599.85052 + float phyResY = vertSizePixel / (vertSizeMM / 25.4f); // = 102.40000 : 128.12611 | 600.95691 + + // ...and rescale the size of the meta rectangle. + float magicX = logResX / phyResX; // = 0.97440946 : 0.93750000 | 1.0002491 + float magicY = logResY / phyResY; // = 0.93750000 : 0.93657720 | 0.99840766 + + // use A4 page in point + // adjust size of A4 page so that meta file fits with DrawImage... + RectF rcMagic(0, 0, magicX * cxPage, magicY * cyPage); + m_PreviewMetafile = new Metafile(hdc, rcMagic, MetafileFrameUnitPoint, + EmfTypeEmfPlusOnly, L"some description"); + + SizeF size; + float horzRes, vertRes; + float height, width; + MetafileHeader metafileHeader; + + // GetPhysicalDimension returns physical size of a pixel in .01 MM units!! + m_PreviewMetafile->GetPhysicalDimension(&size); + + horzRes = (float)m_PreviewMetafile->GetHorizontalResolution(); + vertRes = (float)m_PreviewMetafile->GetVerticalResolution(); + height = (float)m_PreviewMetafile->GetHeight(); + width = (float)m_PreviewMetafile->GetWidth(); + m_PreviewMetafile->GetMetafileHeader(&metafileHeader); + + Graphics gfxMf(m_PreviewMetafile); + dpiX = gfxMf.GetDpiX(); + dpiY = gfxMf.GetDpiY(); + + m_PreviewMetafile->GetPhysicalDimension(&size); + horzRes = (float)m_PreviewMetafile->GetHorizontalResolution(); + vertRes = (float)m_PreviewMetafile->GetVerticalResolution(); + height = (float)m_PreviewMetafile->GetHeight(); + width = (float)m_PreviewMetafile->GetWidth(); + m_PreviewMetafile->GetMetafileHeader(&metafileHeader); + + gfxMf.SetPageUnit(UnitPoint); + if (m_PreviewRenderer && m_PreviewRenderer->CanRender()) + m_PreviewRenderer->Render(&gfxMf, m_Page); + + ::DeleteDC(hdc); + } + if (m_PreviewMetafile) + { + gfx.SetSmoothingMode(SmoothingModeAntiAlias); + PaintBackground(gfx, &virtualPage); + //Matrix matrix(1, 0, 0, 1, (float) - m_posOffset.x, (float) - m_posOffset.y); + m_RenderContext->SetDefaultViewMatrix(&matrix); + gfx.ResetTransform(); + gfx.DrawImage(m_PreviewMetafile, virtualPage); + } + } + break; + + case RenderModeBitmap: + break; + } +#endif + return true; + } + + /// + /// Paints the background and the empty page. + /// + internal void PaintBackground(Graphics gfx) + { + // Draw sharp paper borders and shadow. + gfx.SmoothingMode = SmoothingMode.None; + //gfx.SetCompositingMode(CompositingModeSourceOver); // CompositingModeSourceCopy + //gfx.SetCompositingQuality(CompositingQualityHighQuality); + + gfx.TranslateTransform(-_posOffset.X, -_posOffset.Y); + + // Draw outer area. Use clipping to prevent flickering of page interior. + gfx.SetClip(new Rectangle(_virtualPage.X, _virtualPage.Y, _virtualPage.Width + 3, _virtualPage.Height + 3), CombineMode.Exclude); + gfx.SetClip(new Rectangle(_virtualPage.X + _virtualPage.Width + 1, _virtualPage.Y, 2, 2), CombineMode.Union); + gfx.SetClip(new Rectangle(_virtualPage.X, _virtualPage.Y + _virtualPage.Height + 1, 2, 2), CombineMode.Union); + gfx.Clear(_desktopColor); + +#if DRAW_X + gfx.DrawLine(Pens.Blue, 0, 0, virtualCanvas.Width, virtualCanvas.Height); + gfx.DrawLine(Pens.Blue, virtualCanvas.Width, 0, 0, virtualCanvas.Height); +#endif + gfx.ResetClip(); + +#if !DRAW_BMP + // Fill page interior. + SolidBrush brushPaper = new SolidBrush(_pageColor); + gfx.FillRectangle(brushPaper, _virtualPage.X + 1, _virtualPage.Y + 1, _virtualPage.Width - 1, _virtualPage.Height - 1); +#endif + + //// draw non printable area + //if (m_ShowNonPrintableArea) + //{ + //SolidBrush brushNPA(+DESKTOP::QuerySysColor((SYSCLR_3DLIGHT)) | 0xFF000000); + // + //gfx.FillRectangle(&brushNPA, virtualPage.X, virtualPage.Y, virtualPage.Width, rcPrintableArea.Y - virtualPage.Y); + //gfx.FillRectangle(&brushNPA, virtualPage.X, virtualPage.Y, rcPrintableArea.X - virtualPage.X, virtualPage.Height); + //gfx.FillRectangle(&brushNPA, rcPrintableArea.X + rcPrintableArea.Width, + //virtualPage.Y, virtualPage.X + virtualPage.Width - (rcPrintableArea.X + rcPrintableArea.Width), virtualPage.Height); + //gfx.FillRectangle(&brushNPA, virtualPage.X, rcPrintableArea.Y + rcPrintableArea.Height, + //virtualPage.Width, virtualPage.Y + virtualPage.Height - (rcPrintableArea.Y + rcPrintableArea.Height)); + //} + //DrawDash(gfx, virtualPage); + + // Draw page border and shadow. + Pen penPaperBorder = SystemPens.WindowText; + Brush brushShadow = SystemBrushes.ControlDarkDark; + gfx.DrawRectangle(penPaperBorder, _virtualPage); + gfx.FillRectangle(brushShadow, _virtualPage.X + _virtualPage.Width + 1, _virtualPage.Y + 2, 2, _virtualPage.Height + 1); + gfx.FillRectangle(brushShadow, _virtualPage.X + 2, _virtualPage.Y + _virtualPage.Height + 1, _virtualPage.Width + 1, 2); + } + + /// + /// Check clipping rectangle calculations. + /// + [Conditional("DEBUG")] + void DrawDash(Graphics gfx, Rectangle rect) + { + Pen pen = new Pen(Color.GreenYellow, 1); + pen.DashStyle = DashStyle.Dash; + gfx.DrawRectangle(pen, rect); + } + + /// + /// Adjusts scroll bars. + /// + void SetScrollBarRange() + { + // Windows 7 issue: Must invalidate scroll bars to ensure that + // the arrows are painted correctly. + + Rectangle clientRect = _canvas.ClientRectangle; + Size clientAreaSize = clientRect.Size; + + // Scroll range + int dx = _virtualCanvas.Width - clientAreaSize.Width; + int dy = _virtualCanvas.Height - clientAreaSize.Height; + + //bool extendX = clientAreaSize.Width < virtualCanvas.Width; + //bool extendY = clientAreaSize.Height < virtualCanvas.Height; + + if (ShowScrollbars && _hScrollBar != null) + { + if (_posOffset.X > dx) + _hScrollBar.Value = _posOffset.X = dx; + + if (dx > 0) + { + _hScrollBar.Minimum = 0; + _hScrollBar.Maximum = _virtualCanvas.Width; + _hScrollBar.SmallChange = clientAreaSize.Width / 10; + _hScrollBar.LargeChange = clientAreaSize.Width; + _hScrollBar.Enabled = true; + } + else + { + _hScrollBar.Minimum = 0; + _hScrollBar.Maximum = 0; + _hScrollBar.Enabled = false; + } + _hScrollBar.Invalidate(); + } + + if (ShowScrollbars && _vScrollBar != null) + { + if (_posOffset.Y > dy) + _vScrollBar.Value = _posOffset.Y = dy; + + if (dy > 0) + { + _vScrollBar.Minimum = 0; + _vScrollBar.Maximum = _virtualCanvas.Height; + _vScrollBar.SmallChange = clientAreaSize.Height / 10; + _vScrollBar.LargeChange = clientAreaSize.Height; + _vScrollBar.Enabled = true; + } + else + { + _vScrollBar.Minimum = 0; + _vScrollBar.Maximum = 0; + _vScrollBar.Enabled = false; + } + _vScrollBar.Invalidate(); + } + } + + ///// + ///// Calculates two interesting values... + ///// + //public static void GetMagicValues(IntPtr hdc, out float magicX, out float magicY) + //{ + // // Even Petzold would be surprised about that... + + // // Physical device size in MM + // int horzSizeMM = GetDeviceCaps(hdc, HORZSIZE); + // int vertSizeMM = GetDeviceCaps(hdc, VERTSIZE); + // // + // // Display size in pixels 1600 x 1200 1280 x 1024 + // // + // // My old Sony display with 96 DPI: --- 330 x 254 + // // My old Sony display with 120 DPI: --- 254 x 203 + // // My current Sony display with 96 DPI: 410 x 310 410 x 310 + // // My current Sony display with 120 DPI: 410 x 310 410 x 310 + // // My old Sony display with 96 DPI: --- 360 x 290 + // // My old Sony display with 120 DPI: --- 360 x 290 + // // My LaserJet 6L (300 DPI): 198 (not 210) x 288 (nscot 297) + + + // // Device size in pixel + // int horzSizePixel = GetDeviceCaps(hdc, HORZRES); + // int vertSizePixel = GetDeviceCaps(hdc, VERTRES); + // // + // // Display size in pixels 1600 x 1200 1280 x 1024 + // // + // // My old Sony display with 96 DPI: --- 1280 x 1024 + // // My old Sony display with 120 DPI: --- 1280 x 1024 + // // My current Sony display with 96 DPI: 1600 x 1200 1280 x 1024 + // // My current Sony display with 120 DPI: 1600 x 1200 1280 x 1024 + // // + // // My LaserJet 6L (600 DPI): 4676 x 6814 + + // // 'logical' device resolution in DPI + // int logResX = GetDeviceCaps(hdc, LOGPIXELSX); + // int logResY = GetDeviceCaps(hdc, LOGPIXELSY); + // // + // // Display size in pixels 1600 x 1200 1280 x 1024 + // // + // // My old Sony display with 96 DPI: --- 96 x 96 + // // My old Sony display with 120 DPI: --- 120 x 120 + // // My current Sony display with 96 DPI: 96 x 96 96 x 96 + // // My current Sony display with 120 DPI: 120 x 120 120 x 120 + // // + // // My LaserJet 6L (600 DPI): 600 x 600 + + // // physical pixel size in .01 MM units + // // accidentally(?) the result of GetPhysicalDimension! + // //float X1 = 100.0f * horzSizeMM / horzSizePixel; // = 25.781250 : 19.843750 | 4.2343884 + // //float Y1 = 100.0f * vertSizeMM / vertSizePixel; // = 24.804688 : 19.824219 | 4.2265925 + + // // Now we can get the 'physical' device resolution... + // float phyResX = horzSizePixel / (horzSizeMM / 25.4f); + // float phyResY = vertSizePixel / (vertSizeMM / 25.4f); + // // + // // Display size in pixels 1600 x 1200 1280 x 1024 + // // + // // My old Sony display with 96 DPI: --- 98.521210 x 102.40000 + // // My old Sony display with 120 DPI: --- 128.00000 x 128.12611 + // // My current Sony display with 96 DPI: 99.12195 x 98.32258 79.29756 x 83.90193 + // // My current Sony display with 120 DPI: 99.12195 x 98.32258 79.29756 x 83.90193 + // // + // // My LaserJet 6L (600 DPI): 599.85052 x 600.95691 + + // // ...and rescale the size of the meta rectangle. + // magicX = logResX / phyResX; + // magicY = logResY / phyResY; + // // + // // Display size in pixels 1600 x 1200 1280 x 1024 + // // + // // My old Sony display with 96 DPI: --- 0.97440946 x 0.93750000 + // // My old Sony display with 120 DPI: --- 0.93750000 x 0.93657720 + // // My current Sony display with 96 DPI: 0.968503952 x 0.976377964 1.21062994 x 1.14419293 + // // My current Sony display with 120 DPI: 1.21062994 x 1.22047246 1.51328743 x 1.43024123 + // // + // // My LaserJet 6L (600 DPI): 1.0002491 x 0.99840766 + //} + + //[DllImport("gdi32.dll")] + //static extern int GetDeviceCaps(IntPtr hdc, int capability); + //const int HORZSIZE = 4; + //const int VERTSIZE = 6; + //const int HORZRES = 8; + //const int VERTRES = 10; + //const int LOGPIXELSX = 88; + //const int LOGPIXELSY = 90; + + /// + /// Upper left corner of scroll area. + /// + Point _posOffset; + + /// + /// Real page size in point. + /// + SizeF _pageSize = PageSizeConverter.ToSize(PdfSharp.PageSize.A4).ToSizeF(); + + /// + /// Page in pixel relative to virtual canvas. + /// + Rectangle _virtualPage; + + /// + /// The size in pixels of an area that completely contains the virtual page and at least a small + /// border around it. If this area is larger than the canvas window, it is scrolled. + /// + Size _virtualCanvas; + + /// + /// Printable area in point. + /// + readonly RectangleF _printableArea; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Forms/PagePreview.resx b/src/PDFsharp/src/PdfSharp/Forms/PagePreview.resx new file mode 100644 index 00000000..5d320b1a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/PagePreview.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + False + + + False + + + PagePreview + + + False + + + 80 + + + (Default) + + + False + + + Private + + + 4, 4 + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.cs b/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.cs new file mode 100644 index 00000000..c45a25db --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.cs @@ -0,0 +1,87 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.ComponentModel; +#if GDI +using System.Drawing; +using System.Windows.Forms; +#endif +#if Wpf +using System.Windows.Media; +#endif + +#if !GDI +#error This file must only be included in GDI build. +#endif + +namespace PdfSharp.Forms +{ + /// + /// Implements the control that previews the page. + /// + class PagePreviewCanvas : System.Windows.Forms.Control + { + public PagePreviewCanvas(PagePreview preview) + { + _preview = preview; + SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true); + } + PagePreview _preview; + + protected override void OnPaint(PaintEventArgs e) + { + if (!_preview._showPage) + return; + + Graphics gfx = e.Graphics; + bool zoomChanged; + _preview.CalculatePreviewDimension(out zoomChanged); + _preview.RenderPage(gfx); + } + + protected override void OnPaintBackground(PaintEventArgs e) + { + if (!_preview._showPage) + { + e.Graphics.Clear(_preview._desktopColor); + return; + } + bool zoomChanged; + _preview.CalculatePreviewDimension(out zoomChanged); + _preview.PaintBackground(e.Graphics); + } + + protected override void OnSizeChanged(EventArgs e) + { + Invalidate(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.resx b/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.resx new file mode 100644 index 00000000..3f337e08 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/PagePreviewCanvas.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/PDFsharp/src/PdfSharp/Forms/enums/RenderMode.cs b/src/PDFsharp/src/PdfSharp/Forms/enums/RenderMode.cs new file mode 100644 index 00000000..bd40f237 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/enums/RenderMode.cs @@ -0,0 +1,54 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +namespace PdfSharp.Forms +{ + /// + /// Specifies how to reander the preview. + /// + public enum RenderMode + { + /// + /// Draw immediately. + /// + Direct = 0, + + /// + /// Draw using a metafile. + /// + Metafile = 1, + + /// + /// Draw using a bitmap image. + /// + Bitmap = 2 + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/Forms/enums/Zoom.cs b/src/PDFsharp/src/PdfSharp/Forms/enums/Zoom.cs new file mode 100644 index 00000000..ba1de1bd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Forms/enums/Zoom.cs @@ -0,0 +1,120 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +namespace PdfSharp.Forms +{ + /// + /// Defines a zoom factor used in the preview control. + /// + public enum Zoom + { + /// + /// The smallest possible zoom factor. + /// + Mininum = 10, + + /// + /// The largest possible zoom factor. + /// + Maximum = 800, + + /// + /// A pre-defined zoom factor. + /// + Percent800 = 800, + + /// + /// A pre-defined zoom factor. + /// + Percent600 = 600, + + /// + /// A pre-defined zoom factor. + /// + Percent400 = 400, + + /// + /// A pre-defined zoom factor. + /// + Percent200 = 200, + + /// + /// A pre-defined zoom factor. + /// + Percent150 = 150, + + /// + /// A pre-defined zoom factor. + /// + Percent100 = 100, + + /// + /// A pre-defined zoom factor. + /// + Percent75 = 75, + + /// + /// A pre-defined zoom factor. + /// + Percent50 = 50, + + /// + /// A pre-defined zoom factor. + /// + Percent25 = 25, + + /// + /// A pre-defined zoom factor. + /// + Percent10 = 10, + + /// + /// Sets the zoom factor so that the document fits horizontally into the window. + /// + BestFit = -1, + + /// + /// Sets the zoom factor so that the printable area of the document fits horizontally into the window. + /// Currently not yet implemented and the same as ZoomBestFit. + /// + TextFit = -2, + + /// + /// Sets the zoom factor so that the whole document fits completely into the window. + /// + FullPage = -3, + + /// + /// Sets the zoom factor so that the document is displayed in its real physical size (based on the DPI information returned from the OS for the current monitor). + /// + OriginalSize = -4, + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/Internal/Calc.cs b/src/PDFsharp/src/PdfSharp/Internal/Calc.cs new file mode 100644 index 00000000..c12b52aa --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/Calc.cs @@ -0,0 +1,120 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +#endif +using PdfSharp.Drawing; + +namespace PdfSharp.Internal +{ + /// + /// Some static helper functions for calculations. + /// + internal static class Calc + { + /// + /// Degree to radiant factor. + /// + public const double Deg2Rad = Math.PI / 180; + + ///// + ///// Half of pi. + ///// + //public const double πHalf = Math.PI / 2; + //// α - β κ + + /// + /// Get page size in point from specified PageSize. + /// + public static XSize PageSizeToSize(PageSize value) + { + switch (value) + { + case PageSize.A0: + return new XSize(2380, 3368); + + case PageSize.A1: + return new XSize(1684, 2380); + + case PageSize.A2: + return new XSize(1190, 1684); + + case PageSize.A3: + return new XSize(842, 1190); + + case PageSize.A4: + return new XSize(595, 842); + + case PageSize.A5: + return new XSize(420, 595); + + case PageSize.B4: + return new XSize(729, 1032); + + case PageSize.B5: + return new XSize(516, 729); + + // The strange sizes from overseas... + + case PageSize.Letter: + return new XSize(612, 792); + + case PageSize.Legal: + return new XSize(612, 1008); + + case PageSize.Tabloid: + return new XSize(792, 1224); + + case PageSize.Ledger: + return new XSize(1224, 792); + + case PageSize.Statement: + return new XSize(396, 612); + + case PageSize.Executive: + return new XSize(540, 720); + + case PageSize.Folio: + return new XSize(612, 936); + + case PageSize.Quarto: + return new XSize(610, 780); + + case PageSize.Size10x14: + return new XSize(720, 1008); + } + throw new ArgumentException("Invalid PageSize."); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/ColorHelper.cs b/src/PDFsharp/src/PdfSharp/Internal/ColorHelper.cs new file mode 100644 index 00000000..ef685bb5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/ColorHelper.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +#endif + +#pragma warning disable 649 + +namespace PdfSharp.Internal +{ + struct SColor + { + public byte a; + public byte r; + public byte g; + public byte b; + } + + struct SCColor + { + public float a; + public float r; + public float g; + public float b; + } + + static class ColorHelper + { + public static float sRgbToScRgb(byte bval) + { + float num = ((float)bval) / 255f; + if (num <= 0.0) + return 0f; + if (num <= 0.04045) + return (num / 12.92f); + if (num < 1f) + return (float)Math.Pow((num + 0.055) / 1.055, 2.4); + return 1f; + } + + public static byte ScRgbTosRgb(float val) + { + if (val <= 0.0) + return 0; + if (val <= 0.0031308) + return (byte)(((255f * val) * 12.92f) + 0.5f); + if (val < 1.0) + return (byte)((255f * ((1.055f * ((float)Math.Pow((double)val, 0.41666666666666669))) - 0.055f)) + 0.5f); + return 0xff; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/Diagnostics.cs b/src/PDFsharp/src/PdfSharp/Internal/Diagnostics.cs new file mode 100644 index 00000000..5bee020d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/Diagnostics.cs @@ -0,0 +1,117 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using PdfSharp.Pdf.Content; +using PdfSharp.Pdf.IO; + +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +#endif + +namespace PdfSharp.Internal +{ + enum NotImplementedBehaviour + { + DoNothing, Log, Throw + } + + /// + /// A bunch of internal helper functions. + /// + internal static class Diagnostics + { + public static NotImplementedBehaviour NotImplementedBehaviour + { + get { return _notImplementedBehaviour; } + set { _notImplementedBehaviour = value; } + } + static NotImplementedBehaviour _notImplementedBehaviour; + } + + internal static class ParserDiagnostics + { + public static void ThrowParserException(string message) + { + throw new PdfReaderException(message); + } + + public static void ThrowParserException(string message, Exception innerException) + { + throw new PdfReaderException(message, innerException); + } + + public static void HandleUnexpectedCharacter(char ch) + { + // Hex formatting does not work with type char. It must be casted to integer. + string message = string.Format(CultureInfo.InvariantCulture, + "Unexpected character '0x{0:x4}' in PDF stream. The file may be corrupted. " + + "If you think this is a bug in PDFsharp, please send us your PDF file.", (int)ch); + ThrowParserException(message); + } + public static void HandleUnexpectedToken(string token) + { + string message = string.Format(CultureInfo.InvariantCulture, + "Unexpected token '{0}' in PDF stream. The file may be corrupted. " + + "If you think this is a bug in PDFsharp, please send us your PDF file.", token); + ThrowParserException(message); + } + } + + internal static class ContentReaderDiagnostics + { + public static void ThrowContentReaderException(string message) + { + throw new ContentReaderException(message); + } + + public static void ThrowContentReaderException(string message, Exception innerException) + { + throw new ContentReaderException(message, innerException); + } + + public static void ThrowNumberOutOfIntegerRange(long value) + { + string message = string.Format(CultureInfo.InvariantCulture, "Number '{0}' out of integer range.", value); + ThrowContentReaderException(message); + } + + public static void HandleUnexpectedCharacter(char ch) + { + string message = string.Format(CultureInfo.InvariantCulture, + "Unexpected character '0x{0:x4}' in content stream. The stream may be corrupted or the feature is not implemented. " + + "If you think this is a bug in PDFsharp, please send us your PDF file.", (int)ch); + ThrowContentReaderException(message); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/DiagnosticsHelper.cs b/src/PDFsharp/src/PdfSharp/Internal/DiagnosticsHelper.cs new file mode 100644 index 00000000..4245ecc6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/DiagnosticsHelper.cs @@ -0,0 +1,148 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +#endif +using System.Diagnostics; +using PdfSharp.Drawing; +using PdfSharp.Fonts; + +namespace PdfSharp.Internal +{ + /// + /// A bunch of internal helper functions. + /// + internal static class DiagnosticsHelper + { + public static void HandleNotImplemented(string message) + { + string text = "Not implemented: " + message; + switch (Diagnostics.NotImplementedBehaviour) + { + case NotImplementedBehaviour.DoNothing: + break; + + case NotImplementedBehaviour.Log: + Logger.Log(text); + break; + + case NotImplementedBehaviour.Throw: + ThrowNotImplementedException(text); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Indirectly throws NotImplementedException. + /// Required because PDFsharp Release builds tread warnings as errors and + /// throwing NotImplementedException may lead to unreachable code which + /// crashes the build. + /// + public static void ThrowNotImplementedException(string message) + { + throw new NotImplementedException(message); + } + } + + internal static class Logger + { + public static void Log(string format, params object[] args) + { + Debug.WriteLine("Log..."); + } + } + + class Logging + { + + } + + class Tracing + { + [Conditional("DEBUG")] + public void Foo() + { } + } + + /// + /// Helper class around the Debugger class. + /// + public static class DebugBreak + { + /// + /// Call Debugger.Break() if a debugger is attached. + /// + public static void Break() + { + Break(false); + } + + /// + /// Call Debugger.Break() if a debugger is attached or when always is set to true. + /// + public static void Break(bool always) + { +#if DEBUG + if (always || Debugger.IsAttached) + Debugger.Break(); +#endif + } + } + + /// + /// Internal stuff for development of PDFsharp. + /// + public static class FontsDevHelper + { + /// + /// Creates font and enforces bold/italic simulation. + /// + public static XFont CreateSpecialFont(string familyName, double emSize, XFontStyle style, + XPdfFontOptions pdfOptions, XStyleSimulations styleSimulations) + { + return new XFont(familyName, emSize, style, pdfOptions, styleSimulations); + } + + /// + /// Dumps the font caches to a string. + /// + public static string GetFontCachesState() + { + return FontFactory.GetFontCachesState(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/DoubleUtil.cs b/src/PDFsharp/src/PdfSharp/Internal/DoubleUtil.cs new file mode 100644 index 00000000..f8c96f7d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/DoubleUtil.cs @@ -0,0 +1,208 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Microsoft +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.InteropServices; +#if !EDF_CORE +using PdfSharp.Drawing; +#else +using PdfSharp.Drawing; +#endif + +#if !EDF_CORE +namespace PdfSharp.Internal +#else +namespace Edf.Internal +#endif +{ + /// + /// Some floating point utilities. Partially reflected from WPF, later equalized with original source code. + /// + internal static class DoubleUtil + { + const double Epsilon = 2.2204460492503131E-16; // smallest such that 1.0 + Epsilon != 1.0 + private const double TenTimesEpsilon = 10.0 * Epsilon; + const float FloatMinimum = 1.175494E-38f; + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreClose(double value1, double value2) + { + //if (value1 == value2) + if (value1.Equals(value2)) + return true; + // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < Epsilon + double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * Epsilon; + double delta = value1 - value2; + return (-eps < delta) && (eps > delta); + } + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreRoughlyEqual(double value1, double value2, int decimalPlace) + { + if (value1 == value2) + return true; + return Math.Abs(value1 - value2) < decs[decimalPlace]; + } + static readonly double[] decs = { 1, 1E-1, 1E-2, 1E-3, 1E-4, 1E-5, 1E-6, 1E-7, 1E-8, 1E-9, 1E-10, 1E-11, 1E-12, 1E-13, 1E-14, 1E-15, 1E-16 }; + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreClose(XPoint point1, XPoint point2) + { + return AreClose(point1.X, point2.X) && AreClose(point1.Y, point2.Y); + } + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreClose(XRect rect1, XRect rect2) + { + if (rect1.IsEmpty) + return rect2.IsEmpty; + return !rect2.IsEmpty && AreClose(rect1.X, rect2.X) && AreClose(rect1.Y, rect2.Y) && + AreClose(rect1.Height, rect2.Height) && AreClose(rect1.Width, rect2.Width); + } + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreClose(XSize size1, XSize size2) + { + return AreClose(size1.Width, size2.Width) && AreClose(size1.Height, size2.Height); + } + + /// + /// Indicates whether the values are so close that they can be considered as equal. + /// + public static bool AreClose(XVector vector1, XVector vector2) + { + return AreClose(vector1.X, vector2.X) && AreClose(vector1.Y, vector2.Y); + } + + /// + /// Indicates whether value1 is greater than value2 and the values are not close to each other. + /// + public static bool GreaterThan(double value1, double value2) + { + return value1 > value2 && !AreClose(value1, value2); + } + + /// + /// Indicates whether value1 is greater than value2 or the values are close to each other. + /// + public static bool GreaterThanOrClose(double value1, double value2) + { + return value1 > value2 || AreClose(value1, value2); + } + + /// + /// Indicates whether value1 is less than value2 and the values are not close to each other. + /// + public static bool LessThan(double value1, double value2) + { + return value1 < value2 && !AreClose(value1, value2); + } + + /// + /// Indicates whether value1 is less than value2 or the values are close to each other. + /// + public static bool LessThanOrClose(double value1, double value2) + { + return value1 < value2 || AreClose(value1, value2); + } + + /// + /// Indicates whether the value is between 0 and 1 or close to 0 or 1. + /// + public static bool IsBetweenZeroAndOne(double value) + { + return GreaterThanOrClose(value, 0) && LessThanOrClose(value, 1); + } + + /// + /// Indicates whether the value is not a number. + /// + public static bool IsNaN(double value) + { + NanUnion t = new NanUnion(); + t.DoubleValue = value; + + ulong exp = t.UintValue & 0xfff0000000000000; + ulong man = t.UintValue & 0x000fffffffffffff; + + return (exp == 0x7ff0000000000000 || exp == 0xfff0000000000000) && (man != 0); + } + + /// + /// Indicates whether at least one of the four rectangle values is not a number. + /// + public static bool RectHasNaN(XRect r) + { + return IsNaN(r.X) || IsNaN(r.Y) || IsNaN(r.Height) || IsNaN(r.Width); + } + + /// + /// Indicates whether the value is 1 or close to 1. + /// + public static bool IsOne(double value) + { + return Math.Abs(value - 1.0) < TenTimesEpsilon; + } + + /// + /// Indicates whether the value is 0 or close to 0. + /// + public static bool IsZero(double value) + { + return Math.Abs(value) < TenTimesEpsilon; + } + + /// + /// Converts a double to integer. + /// + public static int DoubleToInt(double value) + { + return 0 < value ? (int)(value + 0.5) : (int)(value - 0.5); + } + + [StructLayout(LayoutKind.Explicit)] + struct NanUnion + { + [FieldOffset(0)] + internal double DoubleValue; + [FieldOffset(0)] + internal readonly ulong UintValue; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/Lock.cs b/src/PDFsharp/src/PdfSharp/Internal/Lock.cs new file mode 100644 index 00000000..4905ff9c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/Lock.cs @@ -0,0 +1,75 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; +using System.Threading; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Internal +{ + /// + /// Static locking functions to make PDFsharp thread save. + /// + internal static class Lock + { + public static void EnterGdiPlus() + { + //if (_fontFactoryLockCount > 0) + // throw new InvalidOperationException(""); + + Monitor.Enter(GdiPlus); + _gdiPlusLockCount++; + } + + public static void ExitGdiPlus() + { + _gdiPlusLockCount--; + Monitor.Exit(GdiPlus); + } + + static readonly object GdiPlus = new object(); + static int _gdiPlusLockCount; + + public static void EnterFontFactory() + { + Monitor.Enter(FontFactory); + _fontFactoryLockCount++; + } + + public static void ExitFontFactory() + { + _fontFactoryLockCount--; + Monitor.Exit(FontFactory); + } + static readonly object FontFactory = new object(); + [ThreadStatic] + static int _fontFactoryLockCount; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Internal/NativeMethods.cs b/src/PDFsharp/src/PdfSharp/Internal/NativeMethods.cs new file mode 100644 index 00000000..42ad1e6b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/NativeMethods.cs @@ -0,0 +1,155 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.InteropServices; + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Internal +{ +#if CORE || GDI || WPF + /// + /// Required native Win32 calls. + /// + static class NativeMethods + { + public const int GDI_ERROR = -1; + + /// + /// Reflected from System.Drawing.SafeNativeMethods+LOGFONT + /// + //[SuppressUnmanagedCodeSecurity] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class LOGFONT + { + // Preserve us for warning CS0649... + LOGFONT(int dummy) + { + lfHeight = 0; + lfWidth = 0; + lfEscapement = 0; + lfOrientation = 0; + lfWeight = 0; + lfItalic = 0; + lfUnderline = 0; + lfStrikeOut = 0; + lfCharSet = 0; + lfOutPrecision = 0; + lfClipPrecision = 0; + lfQuality = 0; + lfPitchAndFamily = 0; + lfFaceName = ""; + } + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceName; + public override string ToString() + { + object[] objArray1 = new object[0x1c] + { + "lfHeight=", lfHeight, + ", lfWidth=", lfWidth, + ", lfEscapement=", lfEscapement, + ", lfOrientation=", lfOrientation, + ", lfWeight=", lfWeight, + ", lfItalic=", lfItalic, + ", lfUnderline=", lfUnderline, + ", lfStrikeOut=", lfStrikeOut, + ", lfCharSet=", lfCharSet, + ", lfOutPrecision=", lfOutPrecision, + ", lfClipPrecision=", lfClipPrecision, + ", lfQuality=", lfQuality, + ", lfPitchAndFamily=", lfPitchAndFamily, + ", lfFaceName=", lfFaceName + }; + return string.Concat(objArray1); + } + public LOGFONT() { } + } + + [DllImport("user32.dll")] + public static extern IntPtr GetDC(IntPtr hwnd); + + [DllImport("user32.dll")] + public static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern int GetFontData( + IntPtr hdc, // handle to DC + uint dwTable, // metric table name + uint dwOffset, // offset into table + byte[] lpvBuffer, // buffer for returned data + int cbData // length of data + ); + + // CreateDCA(__in_opt LPCSTR pwszDriver, __in_opt LPCSTR pwszDevice, __in_opt LPCSTR pszPort, __in_opt CONST DEVMODEA* pdm); + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CreateDC( + string driver, + string device, + string port, + IntPtr data + ); + + [DllImport("gdi32.dll", SetLastError = true)] + public static extern IntPtr CreateCompatibleDC(IntPtr hdc); + + [DllImport("gdi32.dll", EntryPoint = "CreateFontIndirectW")] + public static extern IntPtr CreateFontIndirect(LOGFONT lpLogFont); + + [DllImport("gdi32.dll")] + public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + + [DllImport("gdi32.dll")] + public static extern bool DeleteObject(IntPtr hgdiobj); + + public const int HORZSIZE = 4; // Horizontal size in millimeters + public const int VERTSIZE = 6; // Vertical size in millimeters + public const int HORZRES = 8; // Horizontal width in pixels + public const int VERTRES = 10; // Vertical height in pixels + public const int LOGPIXELSX = 88; // Logical pixels/inch in X + public const int LOGPIXELSY = 90; // Logical pixels/inch in Y + [DllImport("gdi32.dll")] + public static extern int GetDeviceCaps(IntPtr hdc, int nIndex); + } +#endif +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Internal/TokenizerHelper.cs b/src/PDFsharp/src/PdfSharp/Internal/TokenizerHelper.cs new file mode 100644 index 00000000..b443b14f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Internal/TokenizerHelper.cs @@ -0,0 +1,259 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Microsoft +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; + +#if !EDF_CORE +namespace PdfSharp.Internal +#else +namespace Edf.Internal +#endif +{ + // Reflected from WPF to ensure compatibility + // Use netmassdownloader -d "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.0" -output g:\cachetest -v + class TokenizerHelper + { + /// + /// Initializes a new instance of the class. + /// + public TokenizerHelper(string str, IFormatProvider formatProvider) + { + char numericListSeparator = GetNumericListSeparator(formatProvider); + Initialize(str, '\'', numericListSeparator); + } + + /// + /// Initializes a new instance of the class. + /// + public TokenizerHelper(string str, char quoteChar, char separator) + { + Initialize(str, quoteChar, separator); + } + + void Initialize(string str, char quoteChar, char separator) + { + _str = str; + _strLen = str == null ? 0 : str.Length; + _currentTokenIndex = -1; + _quoteChar = quoteChar; + _argSeparator = separator; + + // Skip any whitespace. + while (_charIndex < _strLen) + { + if (!char.IsWhiteSpace(_str, _charIndex)) + return; + _charIndex++; + } + } + + public string NextTokenRequired() + { + if (!NextToken(false)) + throw new InvalidOperationException("PrematureStringTermination"); //SR.Get(SRID.TokenizerHelperPrematureStringTermination, new object[0])); + return GetCurrentToken(); + } + + public string NextTokenRequired(bool allowQuotedToken) + { + if (!NextToken(allowQuotedToken)) + throw new InvalidOperationException("PrematureStringTermination"); //SR.Get(SRID.TokenizerHelperPrematureStringTermination, new object[0])); + return GetCurrentToken(); + } + + public string GetCurrentToken() + { + if (_currentTokenIndex < 0) + return null; + return _str.Substring(_currentTokenIndex, _currentTokenLength); + } + + public void LastTokenRequired() + { + if (_charIndex != _strLen) + throw new InvalidOperationException("Extra data encountered"); //SR.Get(SRID.TokenizerHelperExtraDataEncountered, new object[0])); + } + + /// + /// Move to next token. + /// + public bool NextToken() + { + return NextToken(false); + } + + /// + /// Move to next token. + /// + public bool NextToken(bool allowQuotedToken) + { + return NextToken(allowQuotedToken, _argSeparator); + } + + public bool NextToken(bool allowQuotedToken, char separator) + { + // Reset index. + _currentTokenIndex = -1; + _foundSeparator = false; + + // Already at the end of the string? + if (_charIndex >= _strLen) + return false; + + char currentChar = _str[_charIndex]; + + // Setup the quoteCount . + int quoteCount = 0; + + // If we are allowing a quoted token and this token begins with a quote, + // set up the quote count and skip the initial quote + if (allowQuotedToken && + currentChar == _quoteChar) + { + quoteCount++; + _charIndex++; + } + + int newTokenIndex = _charIndex; + int newTokenLength = 0; + + // Loop until hit end of string or hit a separator or whitespace. + while (_charIndex < _strLen) + { + currentChar = _str[_charIndex]; + + // If have a quoteCount and this is a quote decrement the quoteCount. + if (quoteCount > 0) + { + // If anything but a quoteChar we move on. + if (currentChar == _quoteChar) + { + quoteCount--; + + // If at zero which it always should for now break out of the loop. + if (quoteCount == 0) + { + ++_charIndex; + break; + } + } + } + else if ((char.IsWhiteSpace(currentChar)) || (currentChar == separator)) + { + if (currentChar == separator) + _foundSeparator = true; + break; + } + + _charIndex++; + newTokenLength++; + } + + // If quoteCount isn't zero we hit the end of the string before the ending quote. + if (quoteCount > 0) + throw new InvalidOperationException("Missing end quote"); //SR.Get(SRID.TokenizerHelperMissingEndQuote, new object[0])); + + // Move at the start of the nextToken. + ScanToNextToken(separator); + + // Update the _currentToken values. + _currentTokenIndex = newTokenIndex; + _currentTokenLength = newTokenLength; + + if (_currentTokenLength < 1) + throw new InvalidOperationException("Empty token"); // SR.Get(SRID.TokenizerHelperEmptyToken, new object[0])); + + return true; + } + + private void ScanToNextToken(char separator) + { + // Do nothing if already at end of the string. + if (_charIndex < _strLen) + { + char currentChar = _str[_charIndex]; + + // Ensure that currentChar is a white space or separator. + if (currentChar != separator && !char.IsWhiteSpace(currentChar)) + throw new InvalidOperationException("ExtraDataEncountered"); //SR.Get(SRID.TokenizerHelperExtraDataEncountered, new object[0])); + + // Loop until a character that isn't the separator or white space. + int argSepCount = 0; + while (_charIndex < _strLen) + { + currentChar = _str[_charIndex]; + if (currentChar == separator) + { + _foundSeparator = true; + argSepCount++; + _charIndex++; + + if (argSepCount > 1) + throw new InvalidOperationException("EmptyToken"); //SR.Get(SRID.TokenizerHelperEmptyToken, new object[0])); + } + else if (char.IsWhiteSpace(currentChar)) + { + // Skip white space. + ++_charIndex; + } + else + break; + } + + // If there was a separatorChar then we shouldn't be at the end of string or means there was a separator but there isn't an arg. + if (argSepCount > 0 && _charIndex >= _strLen) + throw new InvalidOperationException("EmptyToken"); // SR.Get(SRID.TokenizerHelperEmptyToken, new object[0])); + } + } + + public static char GetNumericListSeparator(IFormatProvider provider) + { + char numericSeparator = ','; + NumberFormatInfo numberFormat = NumberFormatInfo.GetInstance(provider); + if (numberFormat.NumberDecimalSeparator.Length > 0 && numericSeparator == numberFormat.NumberDecimalSeparator[0]) + numericSeparator = ';'; + return numericSeparator; + } + + public bool FoundSeparator + { + get { return _foundSeparator; } + } + bool _foundSeparator; + + char _argSeparator; + int _charIndex; + int _currentTokenIndex; + int _currentTokenLength; + char _quoteChar; + string _str; + int _strLen; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroField.cs new file mode 100644 index 00000000..59c29696 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroField.cs @@ -0,0 +1,579 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the base class for all interactive field dictionaries. + /// + public abstract class PdfAcroField : PdfDictionary + { + /// + /// Initializes a new instance of PdfAcroField. + /// + internal PdfAcroField(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. Used for type transformation. + /// + protected PdfAcroField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets the name of this field. + /// + public string Name + { + get + { + string name = Elements.GetString(Keys.T); + return name; + } + } + + /// + /// Gets the field flags of this instance. + /// + public PdfAcroFieldFlags Flags + { + // TODO: This entry is inheritable, thus the implementation is incorrect... + get { return (PdfAcroFieldFlags)Elements.GetInteger(Keys.Ff); } + } + + internal PdfAcroFieldFlags SetFlags + { + get { return (PdfAcroFieldFlags)Elements.GetInteger(Keys.Ff); } + set { Elements.SetInteger(Keys.Ff, (int)value); } + } + + /// + /// Gets or sets the value of the field. + /// + public virtual PdfItem Value + { + get { return Elements[Keys.V]; } + set + { + if (ReadOnly) + throw new InvalidOperationException("The field is read only."); + if (value is PdfString || value is PdfName) + Elements[Keys.V] = value; + else + throw new NotImplementedException("Values other than string cannot be set."); + } + } + + /// + /// Gets or sets a value indicating whether the field is read only. + /// + public bool ReadOnly + { + get { return (Flags & PdfAcroFieldFlags.ReadOnly) != 0; } + set + { + if (value) + SetFlags |= PdfAcroFieldFlags.ReadOnly; + else + SetFlags &= ~PdfAcroFieldFlags.ReadOnly; + } + } + + /// + /// Gets the field with the specified name. + /// + public PdfAcroField this[string name] + { + get { return GetValue(name); } + } + + /// + /// Gets a child field by name. + /// + protected virtual PdfAcroField GetValue(string name) + { + if (String.IsNullOrEmpty(name)) + return this; + if (HasKids) + return Fields.GetValue(name); + return null; + } + + /// + /// Indicates whether the field has child fields. + /// + public bool HasKids + { + get + { + PdfItem item = Elements[Keys.Kids]; + if (item == null) + return false; + if (item is PdfArray) + return ((PdfArray)item).Elements.Count > 0; + return false; + } + } + + /// + /// Gets the names of all descendants of this field. + /// + [Obsolete("Use GetDescendantNames")] + public string[] DescendantNames // Properties should not return arrays. + { + get { return GetDescendantNames(); } + } + + /// + /// Gets the names of all descendants of this field. + /// + public string[] GetDescendantNames() + { + List names = new List(); + if (HasKids) + { + PdfAcroFieldCollection fields = Fields; + fields.GetDescendantNames(ref names, null); + } + List temp = new List(); + foreach (string name in names) + temp.Add(name); + return temp.ToArray(); + } + + /// + /// Gets the names of all appearance dictionaries of this AcroField. + /// + public string[] GetAppearanceNames() + { + Dictionary names = new Dictionary(); + PdfDictionary dict = Elements["/AP"] as PdfDictionary; + if (dict != null) + { + AppDict(dict, names); + + if (HasKids) + { + PdfItem[] kids = Fields.Elements.Items; + foreach (PdfItem pdfItem in kids) + { + if (pdfItem is PdfReference) + { + PdfDictionary xxx = ((PdfReference)pdfItem).Value as PdfDictionary; + if (xxx != null) + AppDict(xxx, names); + } + } + //((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + + } + } + string[] array = new string[names.Count]; + names.Keys.CopyTo(array, 0); + return array; + } + + //static string[] AppearanceNames(PdfDictionary dictIn) + //{ + // Dictionary names = new Dictionary(); + // PdfDictionary dict = dictIn["/AP"] as PdfDictionary; + // if (dict != null) + // { + // AppDict(dict, names); + + // if (HasKids) + // { + // PdfItem[] kids = Fields.Elements.Items; + // foreach (PdfItem pdfItem in kids) + // { + // if (pdfItem is PdfReference) + // { + // PdfDictionary xxx = ((PdfReference)pdfItem).Value as PdfDictionary; + // if (xxx != null) + // AppDict(xxx, names); + // } + // } + // //((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + + // } + // } + // string[] array = new string[names.Count]; + // names.Keys.CopyTo(array, 0); + // return array; + //} + + static void AppDict(PdfDictionary dict, Dictionary names) + { + PdfDictionary sub; + if ((sub = dict.Elements["/D"] as PdfDictionary) != null) + AppDict2(sub, names); + if ((sub = dict.Elements["/N"] as PdfDictionary) != null) + AppDict2(sub, names); + } + + static void AppDict2(PdfDictionary dict, Dictionary names) + { + foreach (string key in dict.Elements.Keys) + { + if (!names.ContainsKey(key)) + names.Add(key, null); + } + } + + internal virtual void GetDescendantNames(ref List names, string partialName) + { + if (HasKids) + { + PdfAcroFieldCollection fields = Fields; + string t = Elements.GetString(Keys.T); + Debug.Assert(t != ""); + if (t.Length > 0) + { + if (!String.IsNullOrEmpty(partialName)) + partialName += "." + t; + else + partialName = t; + fields.GetDescendantNames(ref names, partialName); + } + } + else + { + string t = Elements.GetString(Keys.T); + Debug.Assert(t != ""); + if (t.Length > 0) + { + if (!String.IsNullOrEmpty(partialName)) + names.Add(partialName + "." + t); + else + names.Add(t); + } + } + } + + /// + /// Gets the collection of fields within this field. + /// + public PdfAcroFieldCollection Fields + { + get + { + if (_fields == null) + { + object o = Elements.GetValue(Keys.Kids, VCF.CreateIndirect); + _fields = (PdfAcroFieldCollection)o; + } + return _fields; + } + } + PdfAcroFieldCollection _fields; + + /// + /// Holds a collection of interactive fields. + /// + public sealed class PdfAcroFieldCollection : PdfArray + { + PdfAcroFieldCollection(PdfArray array) + : base(array) + { } + + /// + /// Gets the number of elements in the array. + /// + public int Count + { + get + { + return Elements.Count; + } + } + + /// + /// Gets the names of all fields in the collection. + /// + public string[] Names + { + get + { + int count = Elements.Count; + string[] names = new string[count]; + for (int idx = 0; idx < count; idx++) + names[idx] = ((PdfDictionary)((PdfReference)Elements[idx]).Value).Elements.GetString(Keys.T); + return names; + } + } + + /// + /// Gets an array of all descendant names. + /// + public string[] DescendantNames + { + get + { + List names = new List(); + GetDescendantNames(ref names, null); + //List temp = new List(); + //foreach (PdfName name in names) + // temp.Add(name.ToString()); + return names.ToArray(); + } + } + + internal void GetDescendantNames(ref List names, string partialName) + { + int count = Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfAcroField field = this[idx]; + if (field != null) + field.GetDescendantNames(ref names, partialName); + } + } + + /// + /// Gets a field from the collection. For your convenience an instance of a derived class like + /// PdfTextField or PdfCheckBox is returned if PDFsharp can guess the actual type of the dictionary. + /// If the actual type cannot be guessed by PDFsharp the function returns an instance + /// of PdfGenericField. + /// + public PdfAcroField this[int index] + { + get + { + PdfItem item = Elements[index]; + Debug.Assert(item is PdfReference); + PdfDictionary dict = ((PdfReference)item).Value as PdfDictionary; + Debug.Assert(dict != null); + PdfAcroField field = dict as PdfAcroField; + if (field == null && dict != null) + { + // Do type transformation + field = CreateAcroField(dict); + //Elements[index] = field.XRef; + } + return field; + } + } + + /// + /// Gets the field with the specified name. + /// + public PdfAcroField this[string name] + { + get { return GetValue(name); } + } + + internal PdfAcroField GetValue(string name) + { + if (String.IsNullOrEmpty(name)) + return null; + + int dot = name.IndexOf('.'); + string prefix = dot == -1 ? name : name.Substring(0, dot); + string suffix = dot == -1 ? "" : name.Substring(dot + 1); + + int count = Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfAcroField field = this[idx]; + if (field.Name == prefix) + return field.GetValue(suffix); + } + return null; + } + + /// + /// Create a derived type like PdfTextField or PdfCheckBox if possible. + /// If the actual cannot be guessed by PDFsharp the function returns an instance + /// of PdfGenericField. + /// + PdfAcroField CreateAcroField(PdfDictionary dict) + { + string ft = dict.Elements.GetName(Keys.FT); + PdfAcroFieldFlags flags = (PdfAcroFieldFlags)dict.Elements.GetInteger(Keys.Ff); + switch (ft) + { + case "/Btn": + if ((flags & PdfAcroFieldFlags.Pushbutton) != 0) + return new PdfPushButtonField(dict); + + if ((flags & PdfAcroFieldFlags.Radio) != 0) + return new PdfRadioButtonField(dict); + + return new PdfCheckBoxField(dict); + + case "/Tx": + return new PdfTextField(dict); + + case "/Ch": + if ((flags & PdfAcroFieldFlags.Combo) != 0) + return new PdfComboBoxField(dict); + else + return new PdfListBoxField(dict); + + case "/Sig": + return new PdfSignatureField(dict); + + default: + return new PdfGenericField(dict); + } + } + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Required for terminal fields; inheritable) The type of field that this dictionary + /// describes: + /// Btn Button + /// Tx Text + /// Ch Choice + /// Sig (PDF 1.3) Signature + /// Note: This entry may be present in a nonterminal field (one whose descendants + /// are themselves fields) in order to provide an inheritable FT value. However, a + /// nonterminal field does not logically have a type of its own; it is merely a container + /// for inheritable attributes that are intended for descendant terminal fields of + /// any type. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string FT = "/FT"; + + /// + /// (Required if this field is the child of another in the field hierarchy; absent otherwise) + /// The field that is the immediate parent of this one (the field, if any, whose Kids array + /// includes this field). A field can have at most one parent; that is, it can be included + /// in the Kids array of at most one other field. + /// + [KeyInfo(KeyType.Dictionary)] + public const string Parent = "/Parent"; + + /// + /// (Optional) An array of indirect references to the immediate children of this field. + /// + [KeyInfo(KeyType.Array | KeyType.Optional, typeof(PdfAcroFieldCollection))] + public const string Kids = "/Kids"; + + /// + /// (Optional) The partial field name. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string T = "/T"; + + /// + /// (Optional; PDF 1.3) An alternate field name, to be used in place of the actual + /// field name wherever the field must be identified in the user interface (such as + /// in error or status messages referring to the field). This text is also useful + /// when extracting the documents contents in support of accessibility to disabled + /// users or for other purposes. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string TU = "/TU"; + + /// + /// (Optional; PDF 1.3) The mapping name to be used when exporting interactive form field + /// data from the document. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string TM = "/TM"; + + /// + /// (Optional; inheritable) A set of flags specifying various characteristics of the field. + /// Default value: 0. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Ff = "/Ff"; + + /// + /// (Optional; inheritable) The fields value, whose format varies depending on + /// the field type; see the descriptions of individual field types for further information. + /// + [KeyInfo(KeyType.Various | KeyType.Optional)] + public const string V = "/V"; + + /// + /// (Optional; inheritable) The default value to which the field reverts when a + /// reset-form action is executed. The format of this value is the same as that of V. + /// + [KeyInfo(KeyType.Various | KeyType.Optional)] + public const string DV = "/DV"; + + /// + /// (Optional; PDF 1.2) An additional-actions dictionary defining the fields behavior + /// in response to various trigger events. This entry has exactly the same meaning as + /// the AA entry in an annotation dictionary. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string AA = "/AA"; + + // ----- Additional entries to all fields containing variable text -------------------------- + + /// + /// (Required; inheritable) A resource dictionary containing default resources + /// (such as fonts, patterns, or color spaces) to be used by the appearance stream. + /// At a minimum, this dictionary must contain a Font entry specifying the resource + /// name and font dictionary of the default font for displaying the fields text. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string DR = "/DR"; + + /// + /// (Required; inheritable) The default appearance string, containing a sequence of + /// valid page-content graphics or text state operators defining such properties as + /// the fields text size and color. + /// + [KeyInfo(KeyType.String | KeyType.Required)] + public const string DA = "/DA"; + + /// + /// (Optional; inheritable) A code specifying the form of quadding (justification) + /// to be used in displaying the text: + /// 0 Left-justified + /// 1 Centered + /// 2 Right-justified + /// Default value: 0 (left-justified). + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Q = "/Q"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroForm.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroForm.cs new file mode 100644 index 00000000..22c3e7a8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfAcroForm.cs @@ -0,0 +1,151 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents an interactive form (or AcroForm), a collection of fields for + /// gathering information interactively from the user. + /// + public sealed class PdfAcroForm : PdfDictionary + { + /// + /// Initializes a new instance of AcroForm. + /// + internal PdfAcroForm(PdfDocument document) + : base(document) + { + _document = document; + } + + internal PdfAcroForm(PdfDictionary dictionary) + : base(dictionary) + { } + + /// + /// Gets the fields collection of this form. + /// + public PdfAcroField.PdfAcroFieldCollection Fields + { + get + { + if (_fields == null) + { + object o = Elements.GetValue(Keys.Fields, VCF.CreateIndirect); + _fields = (PdfAcroField.PdfAcroFieldCollection)o; + } + return _fields; + } + } + PdfAcroField.PdfAcroFieldCollection _fields; + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public sealed class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Required) An array of references to the documents root fields (those with + /// no ancestors in the field hierarchy). + /// + [KeyInfo(KeyType.Array | KeyType.Required, typeof(PdfAcroField.PdfAcroFieldCollection))] + public const string Fields = "/Fields"; + + /// + /// (Optional) A flag specifying whether to construct appearance streams and + /// appearance dictionaries for all widget annotations in the document. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string NeedAppearances = "/NeedAppearances"; + + /// + /// (Optional; PDF 1.3) A set of flags specifying various document-level characteristics + /// related to signature fields. + /// Default value: 0. + /// + [KeyInfo("1.3", KeyType.Integer | KeyType.Optional)] + public const string SigFlags = "/SigFlags"; + + /// + /// (Required if any fields in the document have additional-actions dictionaries + /// containing a C entry; PDF 1.3) An array of indirect references to field dictionaries + /// with calculation actions, defining the calculation order in which their values will + /// be recalculated when the value of any field changes. + /// + [KeyInfo(KeyType.Array)] + public const string CO = "/CO"; + + /// + /// (Optional) A document-wide default value for the DR attribute of variable text fields. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string DR = "/DR"; + + /// + /// (Optional) A document-wide default value for the DA attribute of variable text fields. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string DA = "/DA"; + + /// + /// (Optional) A document-wide default value for the Q attribute of variable text fields. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Q = "/Q"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get + { + if (s_meta == null) + s_meta = CreateMeta(typeof(Keys)); + return s_meta; + } + } + static DictionaryMeta s_meta; + + // ReSharper restore InconsistentNaming + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfButtonField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfButtonField.cs new file mode 100644 index 00000000..234e2452 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfButtonField.cs @@ -0,0 +1,103 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Pdf.Annotations; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the base class for all button fields. + /// + public abstract class PdfButtonField : PdfAcroField + { + /// + /// Initializes a new instance of the class. + /// + protected PdfButtonField(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. + /// + protected PdfButtonField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets the name which represents the opposite of /Off. + /// + protected string GetNonOffValue() + { + // Try to get the information from the appearance dictionaray. + // Just return the first key that is not /Off. + // I'm not sure what is the right solution to get this value. + PdfDictionary ap = Elements[PdfAnnotation.Keys.AP] as PdfDictionary; + if (ap != null) + { + PdfDictionary n = ap.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + if (name != "/Off") + return name; + } + } + return null; + } + + internal override void GetDescendantNames(ref List names, string partialName) + { + string t = Elements.GetString(PdfAcroField.Keys.T); + // HACK: ??? + if (t == "") + t = "???"; + Debug.Assert(t != ""); + if (t.Length > 0) + { + if (!String.IsNullOrEmpty(partialName)) + names.Add(partialName + "." + t); + else + names.Add(t); + } + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + // Pushbuttons have no additional entries. + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfCheckBoxField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfCheckBoxField.cs new file mode 100644 index 00000000..825e252d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfCheckBoxField.cs @@ -0,0 +1,401 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.Annotations; +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the check box field. + /// + public sealed class PdfCheckBoxField : PdfButtonField + { + /// + /// Initializes a new instance of PdfCheckBoxField. + /// + internal PdfCheckBoxField(PdfDocument document) + : base(document) + { + _document = document; + } + + internal PdfCheckBoxField(PdfDictionary dict) + : base(dict) + { } + +#if true_ + /// + /// Indicates whether the field is checked. + /// + public bool Checked //R080317 // TODO + { + get + { + if (!HasKids) + { + string value = Elements.GetString(Keys.V); + //return !String.IsNullOrEmpty(value) && value != UncheckedValue; + return !String.IsNullOrEmpty(value) && value == CheckedName; + } + + if (Fields.Elements.Items.Length == 2) + { + string value = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.GetString(Keys.V); + //bool bReturn = value.Length != 0 && value != UncheckedValue; //R081114 (3Std.!!) auch auf Nein prfen; //TODO woher kommt der Wert? + bool bReturn = value.Length != 0 && value == CheckedName; + return bReturn; + } + + // NYI: Return false in any other case. + return false; + } + + set + { + if (!HasKids) + { + //string name = value ? GetNonOffValue() : "/Off"; + string name = value ? CheckedName : UncheckedName; + Elements.SetName(Keys.V, name); + Elements.SetName(PdfAnnotation.Keys.AS, name); + } + else + { + // Here we have to handle fields that exist twice with the same name. + // Checked must be set for both fields, using /Off for one field and skipping /Off for the other, + // to have only one field with a check mark. + // Finding this took me two working days. + if (Fields.Elements.Items.Length == 2) + { + if (value) + { + //Element 0 behandeln -> auf checked setzen + string name1 = ""; + PdfDictionary o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + //if (name != UncheckedValue) + if (name == CheckedName) + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + + //Element 1 behandeln -> auf unchecked setzen + o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name == UncheckedName) + { + name1 = name; + break; + } + } + } + } + if (!String.IsNullOrEmpty(name1)) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + } + else + { + //Element 0 behandeln -> auf unchecked setzen + string name1 = ""; + PdfDictionary o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + //if (name != UncheckedValue) + if (name == CheckedName) + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + + //Element 1 behandeln -> auf checked setzen + o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name == UncheckedName) + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + } + } + } + } + } + +#else + /// + /// Indicates whether the field is checked. + /// + public bool Checked + { + get + { + if (!HasKids) //R080317 + { + string value = Elements.GetString(Keys.V); + return value.Length != 0 && value != "/Off"; + } + else //R080317 + { + if (Fields.Elements.Items.Length == 2) + { + string value = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.GetString(Keys.V); + bool bReturn = value.Length != 0 && value != "/Off" && value != "/Nein"; //R081114 (3Std.!!) auch auf Nein prfen; //TODO woher kommt der Wert? + return bReturn; + } + else + return false; + } + } + set + { + if (!HasKids) + { + string name = value ? GetNonOffValue() : "/Off"; + Elements.SetName(Keys.V, name); + Elements.SetName(PdfAnnotation.Keys.AS, name); + } + else + { + // Here we have to handle fields that exist twice with the same name. + // Checked must be set for both fields, using /Off for one field and skipping /Off for the other, + // to have only one field with a check mark. + // Finding this took me two working days. + if (Fields.Elements.Items.Length == 2) + { + if (value) + { + //Element 0 behandeln -> auf checked setzen + string name1 = ""; + PdfDictionary o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name != "/Off") + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + + //Element 1 behandeln -> auf unchecked setzen + o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name == "/Off") + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + + } + else + { + //Element 0 behandeln -> auf unchecked setzen + string name1 = ""; + PdfDictionary o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name != "/Off") + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[1])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + + //Element 1 behandeln -> auf checked setzen + o = ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements["/AP"] as PdfDictionary; + if (o != null) + { + PdfDictionary n = o.Elements["/N"] as PdfDictionary; + if (n != null) + { + foreach (string name in n.Elements.Keys) + { + if (name == "/Off") + { + name1 = name; + break; + } + } + } + } + if (name1.Length != 0) + { + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(Keys.V, name1); + ((PdfDictionary)(((PdfReference)(Fields.Elements.Items[0])).Value)).Elements.SetName(PdfAnnotation.Keys.AS, name1); + } + } + } + } + } + } +#endif + + /// + /// Gets or sets the name of the dictionary that represents the Checked state. + /// + /// The default value is "/Yes". + public string CheckedName + { + get { return _checkedName; } + set { _checkedName = value; } + } + string _checkedName = "/Yes"; + + /// + /// Gets or sets the name of the dictionary that represents the Unchecked state. + /// The default value is "/Off". + /// + public string UncheckedName + { + get { return _uncheckedName; } + set { _uncheckedName = value; } + } + string _uncheckedName = "/Off"; + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfButtonField.Keys + { + /// + /// (Optional; inheritable; PDF 1.4) A text string to be used in place of the V entry for the + /// value of the field. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Opt = "/Opt"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfChoiceField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfChoiceField.cs new file mode 100644 index 00000000..b1594b66 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfChoiceField.cs @@ -0,0 +1,181 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the base class for all choice field dictionaries. + /// + public abstract class PdfChoiceField : PdfAcroField + { + /// + /// Initializes a new instance of the class. + /// + protected PdfChoiceField(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. + /// + protected PdfChoiceField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets the index of the specified string in the /Opt array or -1, if no such string exists. + /// + protected int IndexInOptArray(string value) + { + PdfArray opt = Elements.GetArray(Keys.Opt); + +#if DEBUG // Check with //R080317 implemention + PdfArray opt2 = null; + if (Elements[Keys.Opt] is PdfArray) + opt2 = Elements[Keys.Opt] as PdfArray; + else if (Elements[Keys.Opt] is Advanced.PdfReference) + { + //falls das Array nicht direkt am Element hngt, + //das Array aus dem referenzierten Element holen + opt2 = ((Advanced.PdfReference)Elements[Keys.Opt]).Value as PdfArray; + } + Debug.Assert(ReferenceEquals(opt, opt2)); +#endif + + if (opt != null) + { + int count = opt.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = opt.Elements[idx]; + if (item is PdfString) + { + if (item.ToString() == value) + return idx; + } + else if (item is PdfArray) + { + PdfArray array = (PdfArray)item; + if (array.Elements.Count != 0) + { + if (array.Elements[0].ToString() == value) + return idx; + } + } + } + } + return -1; + } + + /// + /// Gets the value from the index in the /Opt array. + /// + protected string ValueInOptArray(int index) + { + PdfArray opt = Elements.GetArray(Keys.Opt); + if (opt != null) + { + int count = opt.Elements.Count; + if (index < 0 || index >= count) + throw new ArgumentOutOfRangeException("index"); + + PdfItem item = opt.Elements[index]; + if (item is PdfString) + return item.ToString(); + + if (item is PdfArray) + { + PdfArray array = (PdfArray)item; + if (array.Elements.Count != 0) + return array.Elements[0].ToString(); + } + } + return ""; + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + // ReSharper disable InconsistentNaming + + /// + /// (Required; inheritable) An array of options to be presented to the user. Each element of + /// the array is either a text string representing one of the available options or a two-element + /// array consisting of a text string together with a default appearance string for constructing + /// the items appearance dynamically at viewing time. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Opt = "/Opt"; + + /// + /// (Optional; inheritable) For scrollable list boxes, the top index (the index in the Opt array + /// of the first option visible in the list). + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string TI = "/TI"; + + /// + /// (Sometimes required, otherwise optional; inheritable; PDF 1.4) For choice fields that allow + /// multiple selection (MultiSelect flag set), an array of integers, sorted in ascending order, + /// representing the zero-based indices in the Opt array of the currently selected option + /// items. This entry is required when two or more elements in the Opt array have different + /// names but the same export value, or when the value of the choice field is an array; in + /// other cases, it is permitted but not required. If the items identified by this entry differ + /// from those in the V entry of the field dictionary (see below), the V entry takes precedence. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string I = "/I"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + + // ReSharper restore InconsistentNaming + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfComboBoxField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfComboBoxField.cs new file mode 100644 index 00000000..d617754c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfComboBoxField.cs @@ -0,0 +1,132 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the combo box field. + /// + public sealed class PdfComboBoxField : PdfChoiceField + { + /// + /// Initializes a new instance of PdfComboBoxField. + /// + internal PdfComboBoxField(PdfDocument document) + : base(document) + { } + + internal PdfComboBoxField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets the index of the selected item. + /// + public int SelectedIndex + { + get + { + string value = Elements.GetString(Keys.V); + return IndexInOptArray(value); + } + set + { + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + if (value != -1) //R080325 + { + string key = ValueInOptArray(value); + Elements.SetString(Keys.V, key); + Elements.SetInteger("/I", value); //R080304 !!!!!!! sonst reagiert die Combobox berhaupt nicht !!!!! + } + } + } + + /// + /// Gets or sets the value of the field. + /// + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + public override PdfItem Value //R080304 + { + get { return Elements[Keys.V]; } + set + { + if (ReadOnly) + throw new InvalidOperationException("The field is read only."); + if (value is PdfString || value is PdfName) + { + Elements[Keys.V] = value; + SelectedIndex = SelectedIndex; //R080304 !!! + if (SelectedIndex == -1) + { + //R080317 noch nicht rund + try + { + //anhngen + ((PdfArray)(((PdfItem[])(Elements.Values))[2])).Elements.Add(Value); + SelectedIndex = SelectedIndex; + } + catch { } + } + } + else + throw new NotImplementedException("Values other than string cannot be set."); + } + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + // Combo boxes have no additional entries. + + internal static DictionaryMeta Meta + { + get + { + if (Keys._meta == null) + Keys._meta = CreateMeta(typeof(Keys)); + return Keys._meta; + } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfGenericField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfGenericField.cs new file mode 100644 index 00000000..c16cd66a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfGenericField.cs @@ -0,0 +1,69 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents a generic field. Used for AcroForm dictionaries unknown to PDFsharp. + /// + public sealed class PdfGenericField : PdfAcroField + { + /// + /// Initializes a new instance of PdfGenericField. + /// + internal PdfGenericField(PdfDocument document) + : base(document) + { } + + internal PdfGenericField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfListBoxField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfListBoxField.cs new file mode 100644 index 00000000..b4c07aef --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfListBoxField.cs @@ -0,0 +1,88 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the list box field. + /// + public sealed class PdfListBoxField : PdfChoiceField + { + /// + /// Initializes a new instance of PdfListBoxField. + /// + internal PdfListBoxField(PdfDocument document) + : base(document) + { } + + internal PdfListBoxField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets the index of the selected item + /// + public int SelectedIndex + { + get + { + string value = Elements.GetString(Keys.V); + return IndexInOptArray(value); + } + set + { + string key = ValueInOptArray(value); + Elements.SetString(Keys.V, key); + } + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + // List boxes have no additional entries. + + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfPushButtonField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfPushButtonField.cs new file mode 100644 index 00000000..f02cdfd1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfPushButtonField.cs @@ -0,0 +1,71 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the push button field. + /// + public sealed class PdfPushButtonField : PdfButtonField + { + /// + /// Initializes a new instance of PdfPushButtonField. + /// + internal PdfPushButtonField(PdfDocument document) + : base(document) + { + _document = document; + } + + internal PdfPushButtonField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfRadioButtonField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfRadioButtonField.cs new file mode 100644 index 00000000..4b99461d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfRadioButtonField.cs @@ -0,0 +1,132 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the radio button field. + /// + public sealed class PdfRadioButtonField : PdfButtonField + { + /// + /// Initializes a new instance of PdfRadioButtonField. + /// + internal PdfRadioButtonField(PdfDocument document) + : base(document) + { + _document = document; + } + + internal PdfRadioButtonField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets the index of the selected radio button in a radio button group. + /// + public int SelectedIndex + { + get + { + string value = Elements.GetString(Keys.V); + return IndexInOptStrings(value); + } + set + { + PdfArray opt = Elements[Keys.Opt] as PdfArray; + + if (opt == null) + opt = Elements[Keys.Kids] as PdfArray; + + if (opt != null) + { + int count = opt.Elements.Count; + if (value < 0 || value >= count) + throw new ArgumentOutOfRangeException("value"); + Elements.SetName(Keys.V, opt.Elements[value].ToString()); + } + } + } + + int IndexInOptStrings(string value) + { + PdfArray opt = Elements[Keys.Opt] as PdfArray; + if (opt != null) + { + int count = opt.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = opt.Elements[idx]; + if (item is PdfString) + { + if (item.ToString() == value) + return idx; + } + } + } + return -1; + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfButtonField.Keys + { + /// + /// (Optional; inheritable; PDF 1.4) An array of text strings to be used in + /// place of the V entries for the values of the widget annotations representing + /// the individual radio buttons. Each element in the array represents + /// the export value of the corresponding widget annotation in the + /// Kids array of the radio button field. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Opt = "/Opt"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfSignatureField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfSignatureField.cs new file mode 100644 index 00000000..ddade7b1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfSignatureField.cs @@ -0,0 +1,134 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the signature field. + /// + public sealed class PdfSignatureField : PdfAcroField + { + /// + /// Initializes a new instance of PdfSignatureField. + /// + internal PdfSignatureField(PdfDocument document) + : base(document) + { } + + internal PdfSignatureField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be Sig for a signature dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Required; inheritable) The name of the signature handler to be used for + /// authenticating the fields contents, such as Adobe.PPKLite, Entrust.PPKEF, + /// CICI.SignIt, or VeriSign.PPKVS. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Filter = "/Filter"; + + /// + /// (Optional) The name of a specific submethod of the specified handler. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string SubFilter = "/SubFilter"; + + /// + /// (Required) An array of pairs of integers (starting byte offset, length in bytes) + /// describing the exact byte range for the digest calculation. Multiple discontinuous + /// byte ranges may be used to describe a digest that does not include the + /// signature token itself. + /// + [KeyInfo(KeyType.Array | KeyType.Required)] + public const string ByteRange = "/ByteRange"; + + /// + /// (Required) The encrypted signature token. + /// + [KeyInfo(KeyType.String | KeyType.Required)] + public const string Contents = "/Contents"; + + /// + /// (Optional) The name of the person or authority signing the document. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Name = "/Name"; + + /// + /// (Optional) The time of signing. Depending on the signature handler, this + /// may be a normal unverified computer time or a time generated in a verifiable + /// way from a secure time server. + /// + [KeyInfo(KeyType.Date | KeyType.Optional)] + public const string M = "/M"; + + /// + /// (Optional) The CPU host name or physical location of the signing. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Location = "/Location"; + + /// + /// (Optional) The reason for the signing, such as (I agree). + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Reason = "/Reason"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfTextField.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfTextField.cs new file mode 100644 index 00000000..ceb787f6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/PdfTextField.cs @@ -0,0 +1,309 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Annotations; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Represents the text field. + /// + public sealed class PdfTextField : PdfAcroField + { + /// + /// Initializes a new instance of PdfTextField. + /// + internal PdfTextField(PdfDocument document) + : base(document) + { } + + internal PdfTextField(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets the text value of the text field. + /// + public string Text + { + get { return Elements.GetString(Keys.V); } + set { Elements.SetString(Keys.V, value); RenderAppearance(); } //HACK in PdfTextField + } + + /// + /// Gets or sets the font used to draw the text of the field. + /// + public XFont Font + { + get { return _font; } + set { _font = value; } + } + XFont _font = new XFont("Courier New", 10); + + /// + /// Gets or sets the foreground color of the field. + /// + public XColor ForeColor + { + get { return _foreColor; } + set { _foreColor = value; } + } + XColor _foreColor = XColors.Black; + + /// + /// Gets or sets the background color of the field. + /// + public XColor BackColor + { + get { return _backColor; } + set { _backColor = value; } + } + XColor _backColor = XColor.Empty; + + /// + /// Gets or sets the maximum length of the field. + /// + /// The length of the max. + public int MaxLength + { + get { return Elements.GetInteger(Keys.MaxLen); } + set { Elements.SetInteger(Keys.MaxLen, value); } + } + + /// + /// Gets or sets a value indicating whether the field has multiple lines. + /// + public bool MultiLine + { + get { return (Flags & PdfAcroFieldFlags.Multiline) != 0; } + set + { + if (value) + SetFlags |= PdfAcroFieldFlags.Multiline; + else + SetFlags &= ~PdfAcroFieldFlags.Multiline; + } + } + + /// + /// Gets or sets a value indicating whether this field is used for passwords. + /// + public bool Password + { + get { return (Flags & PdfAcroFieldFlags.Password) != 0; } + set + { + if (value) + SetFlags |= PdfAcroFieldFlags.Password; + else + SetFlags &= ~PdfAcroFieldFlags.Password; + } + } + + /// + /// Creates the normal appearance form X object for the annotation that represents + /// this acro form text field. + /// + void RenderAppearance() + { +#if true_ + PdfFormXObject xobj = new PdfFormXObject(Owner); + Owner.Internals.AddObject(xobj); + xobj.Elements["/BBox"] = new PdfLiteral("[0 0 122.653 12.707]"); + xobj.Elements["/FormType"] = new PdfLiteral("1"); + xobj.Elements["/Matrix"] = new PdfLiteral("[1 0 0 1 0 0]"); + PdfDictionary res = new PdfDictionary(Owner); + xobj.Elements["/Resources"] = res; + res.Elements["/Font"] = new PdfLiteral("<< /Helv 28 0 R >> /ProcSet [/PDF /Text]"); + xobj.Elements["/Subtype"] = new PdfLiteral("/Form"); + xobj.Elements["/Type"] = new PdfLiteral("/XObject"); + + string s = + "/Tx BMC " + '\n' + + "q" + '\n' + + "1 1 120.653 10.707 re" + '\n' + + "W" + '\n' + + "n" + '\n' + + "BT" + '\n' + + "/Helv 7.93 Tf" + '\n' + + "0 g" + '\n' + + "2 3.412 Td" + '\n' + + "(Hello ) Tj" + '\n' + + "20.256 0 Td" + '\n' + + "(XXX) Tj" + '\n' + + "ET" + '\n' + + "Q" + '\n' + + "";//"EMC"; + int length = s.Length; + byte[] stream = new byte[length]; + for (int idx = 0; idx < length; idx++) + stream[idx] = (byte)s[idx]; + xobj.CreateStream(stream); + + // Get existing or create new appearance dictionary + PdfDictionary ap = Elements[PdfAnnotation.Keys.AP] as PdfDictionary; + if (ap == null) + { + ap = new PdfDictionary(_document); + Elements[PdfAnnotation.Keys.AP] = ap; + } + + // Set XRef to normal state + ap.Elements["/N"] = xobj.Reference; + + + + + //// HACK + //string m = + //"" + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" PDFsharp 1.40.2150-g (www.pdfsharp.com) (Original: Powered By Crystal) " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" 2011-07-11T23:15:09+02:00 " + '\n' + + //" 2011-05-19T16:26:51+03:00 " + '\n' + + //" 2011-07-11T23:15:09+02:00 " + '\n' + + //" Crystal Reports " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" application/pdf " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" uuid:68249d89-baed-4384-9a2d-fbf8ace75c45 " + '\n' + + //" uuid:3d5f2f46-c140-416f-baf2-7f9c970cef1d " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //" " + '\n' + + //""; + + //PdfDictionary mdict = (PdfDictionary)_document.Internals.GetObject(new PdfObjectID(32)); + + //length = m.Length; + //stream = new byte[length]; + //for (int idx = 0; idx < length; idx++) + // stream[idx] = (byte)m[idx]; + + //mdict.Stream.Value = stream; + + + + +#else + PdfRectangle rect = Elements.GetRectangle(PdfAnnotation.Keys.Rect); + XForm form = new XForm(_document, rect.Size); + XGraphics gfx = XGraphics.FromForm(form); + + if (_backColor != XColor.Empty) + gfx.DrawRectangle(new XSolidBrush(BackColor), rect.ToXRect() - rect.Location); + + string text = Text; + if (text.Length > 0) + gfx.DrawString(Text, Font, new XSolidBrush(ForeColor), + rect.ToXRect() - rect.Location + new XPoint(2, 0), XStringFormats.TopLeft); + + form.DrawingFinished(); + form.PdfForm.Elements.Add("/FormType", new PdfLiteral("1")); + + // Get existing or create new appearance dictionary. + PdfDictionary ap = Elements[PdfAnnotation.Keys.AP] as PdfDictionary; + if (ap == null) + { + ap = new PdfDictionary(_document); + Elements[PdfAnnotation.Keys.AP] = ap; + } + + // Set XRef to normal state + ap.Elements["/N"] = form.PdfForm.Reference; + + PdfFormXObject xobj = form.PdfForm; + string s = xobj.Stream.ToString(); + // Thank you Adobe: Without putting the content in 'EMC brackets' + // the text is not rendered by PDF Reader 9 or higher. + s = "/Tx BMC\n" + s + "\nEMC"; + xobj.Stream.Value = new RawEncoding().GetBytes(s); +#endif + } + + internal override void PrepareForSave() + { + base.PrepareForSave(); + RenderAppearance(); + } + + /// + /// Predefined keys of this dictionary. + /// The description comes from PDF 1.4 Reference. + /// + public new class Keys : PdfAcroField.Keys + { + /// + /// (Optional; inheritable) The maximum length of the fields text, in characters. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string MaxLen = "/MaxLen"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/enums/PdfAcroFieldFlags.cs b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/enums/PdfAcroFieldFlags.cs new file mode 100644 index 00000000..7c8ca3fa --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.AcroForms/enums/PdfAcroFieldFlags.cs @@ -0,0 +1,151 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.AcroForms +{ + /// + /// Specifies the flags of AcroForm fields. + /// + [Flags] + public enum PdfAcroFieldFlags + { + // ----- Common to all fields ----------------------------------------------------------------- + + /// + /// If set, the user may not change the value of the field. Any associated widget + /// annotations will not interact with the user; that is, they will not respond to + /// mouse clicks or change their appearance in response to mouse motions. This + /// flag is useful for fields whose values are computed or imported from a database. + /// + ReadOnly = 1 << (1 - 1), + + /// + /// If set, the field must have a value at the time it is exported by a submit-form action. + /// + Required = 1 << (2 - 1), + + /// + /// If set, the field must not be exported by a submit-form action. + /// + NoExport = 1 << (3 - 1), + + // ----- Specific to button fields ------------------------------------------------------------ + + /// + /// If set, the field is a pushbutton that does not retain a permanent value. + /// + Pushbutton = 1 << (17 - 1), + + /// + /// If set, the field is a set of radio buttons; if clear, the field is a checkbox. + /// This flag is meaningful only if the Pushbutton flag is clear. + /// + Radio = 1 << (16 - 1), + + /// + /// (Radio buttons only) If set, exactly one radio button must be selected at all times; + /// clicking the currently selected button has no effect. If clear, clicking + /// the selected button deselects it, leaving no button selected. + /// + NoToggleToOff = 1 << (15 - 1), + + // ----- Specific to text fields -------------------------------------------------------------- + + /// + /// If set, the field may contain multiple lines of text; if clear, the fields text + /// is restricted to a single line. + /// + Multiline = 1 << (13 - 1), + + /// + /// If set, the field is intended for entering a secure password that should + /// not be echoed visibly to the screen. Characters typed from the keyboard + /// should instead be echoed in some unreadable form, such as + /// asterisks or bullet characters. + /// To protect password confidentiality, viewer applications should never + /// store the value of the text field in the PDF file if this flag is set. + /// + Password = 1 << (14 - 1), + + /// + /// (PDF 1.4) If set, the text entered in the field represents the pathname of + /// a file whose contents are to be submitted as the value of the field. + /// + FileSelect = 1 << (21 - 1), + + /// + /// (PDF 1.4) If set, the text entered in the field will not be spell-checked. + /// + DoNotSpellCheckTextField = 1 << (23 - 1), + + /// + /// (PDF 1.4) If set, the field will not scroll (horizontally for single-line + /// fields, vertically for multiple-line fields) to accommodate more text + /// than will fit within its annotation rectangle. Once the field is full, no + /// further text will be accepted. + /// + DoNotScroll = 1 << (24 - 1), + + // ----- Specific to choice fields ------------------------------------------------------------ + + /// + /// If set, the field is a combo box; if clear, the field is a list box. + /// + Combo = 1 << (18 - 1), + + /// + /// If set, the combo box includes an editable text box as well as a drop list; + /// if clear, it includes only a drop list. This flag is meaningful only if the + /// Combo flag is set. + /// + Edit = 1 << (19 - 1), + + /// + /// If set, the fields option items should be sorted alphabetically. This flag is + /// intended for use by form authoring tools, not by PDF viewer applications; + /// viewers should simply display the options in the order in which they occur + /// in the Opt array. + /// + Sort = 1 << (20 - 1), + + /// + /// (PDF 1.4) If set, more than one of the fields option items may be selected + /// simultaneously; if clear, no more than one item at a time may be selected. + /// + MultiSelect = 1 << (22 - 1), + + /// + /// (PDF 1.4) If set, the text entered in the field will not be spell-checked. + /// This flag is meaningful only if the Combo and Edit flags are both set. + /// + DoNotSpellCheckChoiseField = 1 << (23 - 1), + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfAction.cs b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfAction.cs new file mode 100644 index 00000000..968ffea0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfAction.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Actions +{ + /// + /// Represents the base class for all PDF actions. + /// + public abstract class PdfAction : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + protected PdfAction() + { + Elements.SetName(Keys.Type, "/Action"); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + protected PdfAction(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Action"); + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + /// + /// (Optional) The type of PDF object that this dictionary describes; + /// if present, must be Action for an action dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Action")] + public const string Type = "/Type"; + + /// + /// (Required) The type of action that this dictionary describes. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string S = "/S"; + + /// + /// (Optional; PDF 1.2) The next action or sequence of actions to be performed + /// after the action represented by this dictionary. The value is either a + /// single action dictionary or an array of action dictionaries to be performed + /// in order; see below for further discussion. + /// + [KeyInfo(KeyType.ArrayOrDictionary | KeyType.Optional)] + public const string Next = "/Next"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfEmbeddedGoToAction.cs b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfEmbeddedGoToAction.cs new file mode 100644 index 00000000..fdfb520c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfEmbeddedGoToAction.cs @@ -0,0 +1,273 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Actions +{ + /// + /// Represents a PDF Embedded Goto action. + /// + public sealed class PdfEmbeddedGoToAction : PdfAction + { + /// + /// Initializes a new instance of the class. + /// + public PdfEmbeddedGoToAction() + { + Inititalize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfEmbeddedGoToAction(PdfDocument document) + : base(document) + { + Inititalize(); + } + + /// + /// Creates a link to an embedded document. + /// + /// The path to the named destination through the embedded documents. + /// The path is separated by '\' and the last segment is the name of the named nestination. + /// The other segments describe the route from the current (root or embedded) document to the embedded document holding the destination. + /// ".." references to the parent, other strings refer to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfEmbeddedGoToAction CreatePdfEmbeddedGoToAction(string destinationPath, bool? newWindow = null) + { + return CreatePdfEmbeddedGoToAction(null, destinationPath, newWindow); + } + + /// + /// Creates a link to an embedded document in another document. + /// + /// The path to the target document. + /// The path to the named destination through the embedded documents in the target document. + /// The path is separated by '\' and the last segment is the name of the named destination. + /// The other segments describe the route from the root document to the embedded document. + /// Each segment name refers to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfEmbeddedGoToAction CreatePdfEmbeddedGoToAction(string documentPath, string destinationPath, bool? newWindow = null) + { + PdfEmbeddedGoToAction action = new PdfEmbeddedGoToAction(); + action._documentPath = documentPath; + action._destinationPath = destinationPath; + action._newWindow = newWindow; + return action; + } + string _documentPath; + string _destinationPath; + bool? _newWindow; + + void Inititalize() + { + Elements.SetName(PdfAction.Keys.Type, "/Action"); + Elements.SetName(PdfAction.Keys.S, "/GoToE"); + } + + internal override void WriteObject(PdfWriter writer) + { + if (!string.IsNullOrEmpty(_documentPath)) + { + var encodedPath = EncodePath(_documentPath); + Elements.SetString(Keys.F, encodedPath); + } + + ParseDestinationName(); + + if (_newWindow.HasValue) + Elements.SetBoolean(Keys.NewWindow, _newWindow.Value); + + base.WriteObject(writer); + } + + string EncodePath(string path) + { + var result = path.Replace("\\", "/"); + return result; + } + + void ParseDestinationName() + { + // _destinationPath may contain a path routing through embedded documents. + var segments = _destinationPath.Split(Separator); + + Debug.Assert(segments.Length > 0); + + var currentElementsObject = Elements; + + // Target dictionaries to create for the path: Create a row of TargetDictionaries as we step through the segments. + for (var i = 0; i < segments.Length - 1; i++) + { + var segment = segments[i]; + var target = segment == ParentString + ? TargetDictionary.CreateTargetParent() + : TargetDictionary.CreateTargetChild(segment); + + currentElementsObject.SetObject(Keys.T, target); + + currentElementsObject = target.Elements; + } + + // The destination is the last segment of the path. It has to be saved in the embedded GoTo-Action's Elements. + var destination = segments[segments.Length - 1]; + Elements.SetString(Keys.D, destination); + } + /// + /// Seperator for splitting destination path segments ans destination name. + /// + public const char Separator = '\\'; + /// + /// Path segment string used to move to the parent document. + /// + public const string ParentString = ".."; + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAction.Keys + { + ///// + ///// (Required) The type of action that this dictionary describes; + ///// must be GoToE for an embedded go-to action. + ///// + //[KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "GoToE")] + //public const string S = "/S"; + + /// + /// (Optional) The root document of the target relative to the root document of the source. + /// If this entry is absent, the source and target share the same root document. + /// + [KeyInfo(KeyType.FileSpecification | KeyType.Optional)] + public const string F = "/F"; + + /// + /// (Required) The destination in the target to jump to (see Section 8.2.1, Destinations). + /// + [KeyInfo(KeyType.Name | KeyType.ByteString | KeyType.Array | KeyType.Required)] + public const string D = "/D"; + + /// + /// (Optional) If true, the destination document should be opened in a new window; + /// if false, the destination document should replace the current document in the same window. + /// If this entry is absent, the viewer application should honor the current user preference. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string NewWindow = "/NewWindow"; + + /// + /// (Optional if F is present; otherwise required) A target dictionary (see Table 8.52) + /// specifying path information to the target document. Each target dictionary specifies + /// one element in the full path to the target and may have nested target dictionaries + /// specifying additional elements. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string T = "/T"; + } + + internal class TargetDictionary : PdfDictionary + { + TargetDictionary() + { } + + public static TargetDictionary CreateTargetChild(string name) + { + var target = new TargetDictionary(); + + target.Elements.SetName(Keys.R, "/C"); + target.Elements.SetString(Keys.N, name); + + return target; + } + + public static TargetDictionary CreateTargetParent() + { + var target = new TargetDictionary(); + + target.Elements.SetName(Keys.R, "/P"); + + return target; + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + /// + /// (Required) Specifies the relationship between the current document and the target + /// (which may be an intermediate target). Valid values are P (the target is the parent + /// of the current document) and C (the target is a child of the current document). + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string R = "/R"; + + /// + /// (Required if the value of R is C and the target is located in the EmbeddedFiles name tree; + /// otherwise, it must be absent) The name of the file in the EmbeddedFiles name tree. + /// + [KeyInfo(KeyType.ByteString | KeyType.Optional)] + public const string N = "/N"; + + ///// + ///// (Required if the value of R is C and the target is associated with a file attachment annotation; + ///// otherwise, it must be absent) If the value is an integer, it specifies the page number (zero-based) + ///// in the current document containing the file attachment annotation. If the value is a string, + ///// it specifies a named destination in the current document that provides the page number of the + ///// file attachment annotation. + ///// + //[KeyInfo(KeyType.Integer | KeyType.ByteString | KeyType.Optional)] + //public const string P = "/P"; + + ///// + ///// (Required if the value of R is C and the target is associated with a file attachment annotation; + ///// otherwise, it must be absent) If the value is an integer, it specifies the index (zero-based) + ///// of the annotation in the Annots array (see Table 3.27) of the page specified by P. If the value + ///// is a text string, it specifies the value of NM in the annotation dictionary (see Table 8.15). + ///// + //[KeyInfo(KeyType.Integer | KeyType.TextString | KeyType.Optional)] + //public const string A = "/A"; + + /// + /// (Optional) A target dictionary specifying additional path information to the target document. + /// If this entry is absent, the current document is the target file containing the destination. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string T = "/T"; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfGoToAction.cs b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfGoToAction.cs new file mode 100644 index 00000000..cc011430 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfGoToAction.cs @@ -0,0 +1,101 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Actions +{ + /// + /// Represents a PDF Goto action. + /// + public sealed class PdfGoToAction : PdfAction + { + /// + /// Initializes a new instance of the class. + /// + public PdfGoToAction() + { + Inititalize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfGoToAction(PdfDocument document) + : base(document) + { + Inititalize(); + } + + /// + /// Creates a link within the current document. + /// + /// The Named Destination's name in the target document. + public static PdfGoToAction CreateGoToAction(string destinationName) + { + PdfGoToAction action = new PdfGoToAction(); + action._destinationName = destinationName; + return action; + } + string _destinationName; + + void Inititalize() + { + Elements.SetName(PdfAction.Keys.Type, "/Action"); + Elements.SetName(PdfAction.Keys.S, "/GoTo"); + } + + internal override void WriteObject(PdfWriter writer) + { + Elements.SetString(PdfRemoteGoToAction.Keys.D, _destinationName); + + base.WriteObject(writer); + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAction.Keys + { + ///// + ///// (Required) The type of action that this dictionary describes; + ///// must be GoTo for a go-to action. + ///// + //[KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "GoTo")] + //public const string S = "/S"; + + /// + /// (Required) The destination to jump to (see Section 8.2.1, Destinations). + /// + [KeyInfo(KeyType.Name | KeyType.ByteString | KeyType.Array | KeyType.Required)] + public const string D = "/D"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfRemoteGoToAction.cs b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfRemoteGoToAction.cs new file mode 100644 index 00000000..eda6f354 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Actions/PdfRemoteGoToAction.cs @@ -0,0 +1,138 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Actions +{ + /// + /// Represents a PDF Remote Goto action. + /// + public sealed class PdfRemoteGoToAction : PdfAction + { + /// + /// Initializes a new instance of the class. + /// + public PdfRemoteGoToAction() + { + Inititalize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfRemoteGoToAction(PdfDocument document) + : base(document) + { + Inititalize(); + } + + /// + /// Creates a link to another document. + /// + /// The path to the target document. + /// The named destination's name in the target document. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfRemoteGoToAction CreateRemoteGoToAction(string documentPath, string destinationName, bool? newWindow = null) + { + PdfRemoteGoToAction action = new PdfRemoteGoToAction(); + action._documentPath = documentPath; + action._destinationName = destinationName; + action._newWindow = newWindow; + return action; + } + string _documentPath; + string _destinationName; + bool? _newWindow; + + void Inititalize() + { + Elements.SetName(PdfAction.Keys.Type, "/Action"); + Elements.SetName(PdfAction.Keys.S, "/GoToR"); + } + + internal override void WriteObject(PdfWriter writer) + { + var encodedPath = EncodePath(_documentPath); + Elements.SetString(Keys.F, encodedPath); + + Elements.SetString(Keys.D, _destinationName); + + if (_newWindow.HasValue) + Elements.SetBoolean(Keys.NewWindow, _newWindow.Value); + + base.WriteObject(writer); + } + + string EncodePath(string path) + { + var result = path.Replace("\\", "/"); + return result; + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAction.Keys + { + ///// + ///// (Required) The type of action that this dictionary describes; + ///// must be GoToR for a remote go-to action. + ///// + //[KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "GoToR")] + //public const string S = "/S"; + + /// + /// (Required) The destination to jump to (see Section 8.5.3, Action Types). + /// + [KeyInfo(KeyType.String | KeyType.Dictionary | KeyType.Required)] + //[KeyInfo(KeyType.FileSpecification | KeyType.Required)] // File Specifications are not yet implemented. + public const string F = "/F"; + + /// + /// (Required) The destination to jump to (see Section 8.2.1, Destinations). + /// If the value is an array defining an explicit destination (as described under Explicit Destinations on page 582), + /// its first element must be a page number within the remote document rather than an indirect reference to a page object + /// in the current document. The first page is numbered 0. + /// + [KeyInfo(KeyType.Name | KeyType.ByteString | KeyType.Array | KeyType.Required)] + public const string D = "/D"; + + /// + /// (Optional; PDF 1.2) A flag specifying whether to open the destination document in a new window. + /// If this flag is false, the destination document replaces the current document in the same window. + /// If this entry is absent, the viewer application should behave in accordance with the current user preference. + /// + [KeyInfo("1.2", KeyType.Boolean | KeyType.Optional)] + public const string NewWindow = "/NewWindow"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Actions/enums/PdfNamedActionNames.cs b/src/PDFsharp/src/PdfSharp/Pdf.Actions/enums/PdfNamedActionNames.cs new file mode 100644 index 00000000..a592e35a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Actions/enums/PdfNamedActionNames.cs @@ -0,0 +1,57 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Actions +{ + /// + /// Specifies the predefined PDF actions. + /// + public enum PdfNamedActionNames + { + /// + /// Go to next page. + /// + NextPage, + + /// + /// Go to previous page. + /// + PrevPage, + + /// + /// Go to first page. + /// + FirstPage, + + /// + /// Go to last page. + /// + LastPage + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/IContentStream.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/IContentStream.cs new file mode 100644 index 00000000..5a20e54c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/IContentStream.cs @@ -0,0 +1,48 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + + + +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + internal interface IContentStream + { + PdfResources Resources { get; } + + string GetFontName(XFont font, out PdfFont pdfFont); + + string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont); + + string GetImageName(XImage image); + + string GetFormName(XForm form); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCIDFont.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCIDFont.cs new file mode 100644 index 00000000..eec0e715 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCIDFont.cs @@ -0,0 +1,233 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Drawing; +using PdfSharp.Pdf.Filters; +using PdfSharp.Fonts.OpenType; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a CIDFont dictionary. + /// + internal class PdfCIDFont : PdfFont + { + public PdfCIDFont(PdfDocument document) + : base(document) + { } + + public PdfCIDFont(PdfDocument document, PdfFontDescriptor fontDescriptor, XFont font) + : base(document) + { + Elements.SetName(Keys.Type, "/Font"); + Elements.SetName(Keys.Subtype, "/CIDFontType2"); + PdfDictionary cid = new PdfDictionary(); + cid.Elements.SetString("/Ordering", "Identity"); + cid.Elements.SetString("/Registry", "Adobe"); + cid.Elements.SetInteger("/Supplement", 0); + Elements.SetValue(Keys.CIDSystemInfo, cid); + // @PDF/UA / 'Identity' or a stream must obviously be set for CIDFonts to satisfy PDF/UA requirements. + Elements.SetName(Keys.CIDToGIDMap, "Identity"); + + FontDescriptor = fontDescriptor; + // ReSharper disable once DoNotCallOverridableMethodsInConstructor + Owner._irefTable.Add(fontDescriptor); + Elements[Keys.FontDescriptor] = fontDescriptor.Reference; + + FontEncoding = font.PdfOptions.FontEncoding; + } + + public PdfCIDFont(PdfDocument document, PdfFontDescriptor fontDescriptor, byte[] fontData) + : base(document) + { + Elements.SetName(Keys.Type, "/Font"); + Elements.SetName(Keys.Subtype, "/CIDFontType2"); + PdfDictionary cid = new PdfDictionary(); + cid.Elements.SetString("/Ordering", "Identity"); + cid.Elements.SetString("/Registry", "Adobe"); + cid.Elements.SetInteger("/Supplement", 0); + Elements.SetValue(Keys.CIDSystemInfo, cid); + // @PDF/UA / 'Identity' or a stream must obviously be set for CIDFonts to satisfy PDF/UA requirements. + Elements.SetName(Keys.CIDToGIDMap, "Identity"); + + FontDescriptor = fontDescriptor; + // ReSharper disable once DoNotCallOverridableMethodsInConstructor + Owner._irefTable.Add(fontDescriptor); + Elements[Keys.FontDescriptor] = fontDescriptor.Reference; + + FontEncoding = PdfFontEncoding.Unicode; + } + + public string BaseFont + { + get { return Elements.GetName(Keys.BaseFont); } + set { Elements.SetName(Keys.BaseFont, value); } + } + + /// + /// Prepares the object to get saved. + /// + internal override void PrepareForSave() + { + base.PrepareForSave(); + +#if DEBUG_ + if (FontDescriptor._descriptor.FontFace.loca == null) + { + GetType(); + } +#endif + // CID fonts must be always embedded. PDFsharp embeds automatically a subset. + OpenTypeFontface subSet = null; + if (FontDescriptor._descriptor.FontFace.loca == null) + subSet = FontDescriptor._descriptor.FontFace; + else + subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, true); + byte[] fontData = subSet.FontSource.Bytes; + PdfDictionary fontStream = new PdfDictionary(Owner); + Owner.Internals.AddObject(fontStream); + FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference; + + fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length); + if (!Owner.Options.NoCompression) + { + fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode); + fontStream.Elements["/Filter"] = new PdfName("/FlateDecode"); + } + fontStream.Elements["/Length"] = new PdfInteger(fontData.Length); + fontStream.CreateStream(fontData); + } + + /// + /// Predefined keys of this dictionary. + /// + public new sealed class Keys : PdfFont.Keys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Font for a CIDFont dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Font")] + public new const string Type = "/Type"; + + /// + /// (Required) The type of CIDFont; CIDFontType0 or CIDFontType2. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string Subtype = "/Subtype"; + + /// + /// (Required) The PostScript name of the CIDFont. For Type 0 CIDFonts, this + /// is usually the value of the CIDFontName entry in the CIDFont program. For + /// Type 2 CIDFonts, it is derived the same way as for a simple TrueType font; + /// In either case, the name can have a subset prefix if appropriate. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string BaseFont = "/BaseFont"; + + /// + /// (Required) A dictionary containing entries that define the character collection + /// of the CIDFont. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string CIDSystemInfo = "/CIDSystemInfo"; + + /// + /// (Required; must be an indirect reference) A font descriptor describing the + /// CIDFont’s default metrics other than its glyph widths. + /// + [KeyInfo(KeyType.Dictionary | KeyType.MustBeIndirect, typeof(PdfFontDescriptor))] + public new const string FontDescriptor = "/FontDescriptor"; + + /// + /// (Optional) The default width for glyphs in the CIDFont. + /// Default value: 1000. + /// + [KeyInfo(KeyType.Integer)] + public const string DW = "/DW"; + + /// + /// (Optional) A description of the widths for the glyphs in the CIDFont. The + /// array’s elements have a variable format that can specify individual widths + /// for consecutive CIDs or one width for a range of CIDs. + /// Default value: none (the DW value is used for all glyphs). + /// + [KeyInfo(KeyType.Array, typeof(PdfArray))] + public const string W = "/W"; + + /// + /// (Optional; applies only to CIDFonts used for vertical writing) An array of two + /// numbers specifying the default metrics for vertical writing. + /// Default value: [880 −1000]. + /// + [KeyInfo(KeyType.Array)] + public const string DW2 = "/DW2"; + + /// + /// (Optional; applies only to CIDFonts used for vertical writing) A description + /// of the metrics for vertical writing for the glyphs in the CIDFont. + /// Default value: none (the DW2 value is used for all glyphs). + /// + [KeyInfo(KeyType.Array, typeof(PdfArray))] + public const string W2 = "/W2"; + + /// + /// (Optional; Type 2 CIDFonts only) A specification of the mapping from CIDs + /// to glyph indices. If the value is a stream, the bytes in the stream contain the + /// mapping from CIDs to glyph indices: the glyph index for a particular CID + /// value c is a 2-byte value stored in bytes 2 × c and 2 × c + 1, where the first + /// byte is the high-order byte. If the value of CIDToGIDMap is a name, it must + /// be Identity, indicating that the mapping between CIDs and glyph indices is + /// the identity mapping. + /// Default value: Identity. + /// This entry may appear only in a Type 2 CIDFont whose associated True-Type font + /// program is embedded in the PDF file. + /// + [KeyInfo(KeyType.Dictionary | KeyType.StreamOrName)] + public const string CIDToGIDMap = "/CIDToGIDMap"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCatalog.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCatalog.cs new file mode 100644 index 00000000..4033bdfb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCatalog.cs @@ -0,0 +1,488 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.AcroForms; +using PdfSharp.Pdf.Structure; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents the catalog dictionary. + /// + public sealed class PdfCatalog : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfCatalog(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Catalog"); + + _version = "1.4"; // HACK in PdfCatalog + } + + internal PdfCatalog(PdfDictionary dictionary) + : base(dictionary) + { } + + /// + /// Get or sets the version of the PDF specification to which the document conforms. + /// + public string Version + { + get { return _version; } + set + { + switch (value) + { + case "1.0": + case "1.1": + case "1.2": + throw new InvalidOperationException("Unsupported PDF version."); + + case "1.3": + case "1.4": + _version = value; + break; + + case "1.5": + case "1.6": + throw new InvalidOperationException("Unsupported PDF version."); + + default: + throw new ArgumentException("Invalid version."); + } + } + } + string _version = "1.3"; + + /// + /// Gets the pages collection of this document. + /// + public PdfPages Pages + { + get + { + if (_pages == null) + { + _pages = (PdfPages)Elements.GetValue(Keys.Pages, VCF.CreateIndirect); + if (Owner.IsImported) + _pages.FlattenPageTree(); + } + return _pages; + } + } + PdfPages _pages; + + /// + /// Implementation of PdfDocument.PageLayout. + /// + internal PdfPageLayout PageLayout + { + get { return (PdfPageLayout)Elements.GetEnumFromName(Keys.PageLayout, PdfPageLayout.SinglePage); } + set { Elements.SetEnumAsName(Keys.PageLayout, value); } + } + + /// + /// Implementation of PdfDocument.PageMode. + /// + internal PdfPageMode PageMode + { + get { return (PdfPageMode)Elements.GetEnumFromName(Keys.PageMode, PdfPageMode.UseNone); } + set { Elements.SetEnumAsName(Keys.PageMode, value); } + } + + /// + /// Implementation of PdfDocument.ViewerPreferences. + /// + internal PdfViewerPreferences ViewerPreferences + { + get + { + if (_viewerPreferences == null) + _viewerPreferences = (PdfViewerPreferences)Elements.GetValue(Keys.ViewerPreferences, VCF.CreateIndirect); + return _viewerPreferences; + } + } + PdfViewerPreferences _viewerPreferences; + + /// + /// Implementation of PdfDocument.Outlines. + /// + internal PdfOutlineCollection Outlines + { + get + { + if (_outline == null) + { + ////// Ensure that the page tree exists. + ////// ReSharper disable once UnusedVariable because we need dummy to call the getter. + ////PdfPages dummy = Pages; + + // Now create the outline item tree. + _outline = (PdfOutline)Elements.GetValue(Keys.Outlines, VCF.CreateIndirect); + } + return _outline.Outlines; + } + } + PdfOutline _outline; + + /// + /// Gets the name dictionary of this document. + /// + public PdfNameDictionary Names + { + get + { + if (_names == null) + { + _names = new PdfNameDictionary(Owner); + Owner.Internals.AddObject(_names); + Elements.SetReference(Keys.Names, _names.Reference); + + } + return _names; + } + } + PdfNameDictionary _names; + + /// + /// Gets the AcroForm dictionary of this document. + /// + public PdfAcroForm AcroForm + { + get + { + if (_acroForm == null) + _acroForm = (PdfAcroForm)Elements.GetValue(Keys.AcroForm); + return _acroForm; + } + } + PdfAcroForm _acroForm; + + /// + /// Gets or sets the language identifier specifying the natural language for all text in the document. + /// Sample values are 'en-US' for 'English United States' or 'de-DE' for 'deutsch Deutschland' (i.e. 'German Germany'). + /// + public string Language + { + get { return Elements.GetString(Keys.Lang); } + set + { + if (value == null) + Elements.Remove(Keys.Lang); + else + Elements.SetString(Keys.Lang, value); + } + } + + /// + /// Dispatches PrepareForSave to the objects that need it. + /// + internal override void PrepareForSave() + { + // Prepare pages. + if (_pages != null) + _pages.PrepareForSave(); + + // Create outline objects. + if (_outline != null && _outline.Outlines.Count > 0) + { + if (Elements[Keys.PageMode] == null) + PageMode = PdfPageMode.UseOutlines; + _outline.PrepareForSave(); + } + + // Clean up structure tree root. + PdfStructureTreeRoot str = Elements.GetObject(Keys.StructTreeRoot) as PdfStructureTreeRoot; + if (str != null) + str.PrepareForSave(); + } + + internal override void WriteObject(PdfWriter writer) + { + if (_outline != null && _outline.Outlines.Count > 0) + { + if (Elements[Keys.PageMode] == null) + PageMode = PdfPageMode.UseOutlines; + } + base.WriteObject(writer); + } + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Catalog for the catalog dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Catalog")] + public const string Type = "/Type"; + + /// + /// (Optional; PDF 1.4) The version of the PDF specification to which the document + /// conforms (for example, 1.4) if later than the version specified in the files header. + /// If the header specifies a later version, or if this entry is absent, the document + /// conforms to the version specified in the header. This entry enables a PDF producer + /// application to update the version using an incremental update. + /// + [KeyInfo("1.4", KeyType.Name | KeyType.Optional)] + public const string Version = "/Version"; + + /// + /// (Required; must be an indirect reference) The page tree node that is the root of + /// the documents page tree. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required | KeyType.MustBeIndirect, typeof(PdfPages))] + public const string Pages = "/Pages"; + + /// + /// (Optional; PDF 1.3) A number tree defining the page labeling for the document. + /// The keys in this tree are page indices; the corresponding values are page label dictionaries. + /// Each page index denotes the first page in a labeling range to which the specified page + /// label dictionary applies. The tree must include a value for pageindex 0. + /// + [KeyInfo("1.3", KeyType.NumberTree | KeyType.Optional)] + public const string PageLabels = "/PageLabels"; + + /// + /// (Optional; PDF 1.2) The documents name dictionary. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional)] + public const string Names = "/Names"; + + /// + /// (Optional; PDF 1.1; must be an indirect reference) A dictionary of names and + /// corresponding destinations. + /// + [KeyInfo("1.1", KeyType.Dictionary | KeyType.Optional)] + public const string Dests = "/Dests"; + + /// + /// (Optional; PDF 1.2) A viewer preferences dictionary specifying the way the document + /// is to be displayed on the screen. If this entry is absent, applications should use + /// their own current user preference settings. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional, typeof(PdfViewerPreferences))] + public const string ViewerPreferences = "/ViewerPreferences"; + + /// + /// (Optional) A name object specifying the page layout to be used when the document is + /// opened: + /// SinglePage - Display one page at a time. + /// OneColumn - Display the pages in one column. + /// TwoColumnLeft - Display the pages in two columns, with oddnumbered pages on the left. + /// TwoColumnRight - Display the pages in two columns, with oddnumbered pages on the right. + /// TwoPageLeft - (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left + /// TwoPageRight - (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string PageLayout = "/PageLayout"; + + /// + /// (Optional) A name object specifying how the document should be displayed when opened: + /// UseNone - Neither document outline nor thumbnail images visible. + /// UseOutlines - Document outline visible. + /// UseThumbs - Thumbnail images visible. + /// FullScreen - Full-screen mode, with no menu bar, windowcontrols, or any other window visible. + /// UseOC - (PDF 1.5) Optional content group panel visible. + /// UseAttachments (PDF 1.6) Attachments panel visible. + /// Default value: UseNone. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string PageMode = "/PageMode"; + + /// + /// (Optional; must be an indirect reference) The outline dictionary that is the root + /// of the documents outline hierarchy. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfOutline))] + public const string Outlines = "/Outlines"; + + /// + /// (Optional; PDF 1.1; must be an indirect reference) An array of thread dictionaries + /// representing the documents article threads. + /// + [KeyInfo("1.1", KeyType.Array | KeyType.Optional)] + public const string Threads = "/Threads"; + + /// + /// (Optional; PDF 1.1) A value specifying a destination to be displayed or an action to be + /// performed when the document is opened. The value is either an array defining a destination + /// or an action dictionary representing an action. If this entry is absent, the document + /// should be opened to the top of the first page at the default magnification factor. + /// + [KeyInfo("1.1", KeyType.ArrayOrDictionary | KeyType.Optional)] + public const string OpenAction = "/OpenAction"; + + /// + /// (Optional; PDF 1.4) An additional-actions dictionary defining the actions to be taken + /// in response to various trigger events affecting the document as a whole. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional)] + public const string AA = "/AA"; + + /// + /// (Optional; PDF 1.1) A URI dictionary containing document-level information for URI + /// (uniform resource identifier) actions. + /// + [KeyInfo("1.1", KeyType.Dictionary | KeyType.Optional)] + public const string URI = "/URI"; + + /// + /// (Optional; PDF 1.2) The documents interactive form (AcroForm) dictionary. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional, typeof(PdfAcroForm))] + public const string AcroForm = "/AcroForm"; + + /// + /// (Optional; PDF 1.4; must be an indirect reference) A metadata stream + /// containing metadata for the document. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional | KeyType.MustBeIndirect)] + public const string Metadata = "/Metadata"; + + /// + /// (Optional; PDF 1.3) The documents structure tree root dictionary. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional)] + public const string StructTreeRoot = "/StructTreeRoot"; + + /// + /// (Optional; PDF 1.4) A mark information dictionary containing information + /// about the documents usage of Tagged PDF conventions. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional)] + public const string MarkInfo = "/MarkInfo"; + + /// + /// (Optional; PDF 1.4) A language identifier specifying the natural language for all + /// text in the document except where overridden by language specifications for structure + /// elements or marked content. If this entry is absent, the language is considered unknown. + /// + [KeyInfo("1.4", KeyType.String | KeyType.Optional)] + public const string Lang = "/Lang"; + + /// + /// (Optional; PDF 1.3) A Web Capture information dictionary containing state information + /// used by the Acrobat Web Capture (AcroSpider) plugin extension. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional)] + public const string SpiderInfo = "/SpiderInfo"; + + /// + /// (Optional; PDF 1.4) An array of output intent dictionaries describing the color + /// characteristics of output devices on which the document might be rendered. + /// + [KeyInfo("1.4", KeyType.Array | KeyType.Optional)] + public const string OutputIntents = "/OutputIntents"; + + /// + /// (Optional; PDF 1.4) A page-piece dictionary associated with the document. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional)] + public const string PieceInfo = "/PieceInfo"; + + /// + /// (Optional; PDF 1.5; required if a document contains optional content) The documents + /// optional content properties dictionary. + /// + [KeyInfo("1.5", KeyType.Dictionary | KeyType.Optional)] + public const string OCProperties = "/OCProperties"; + + /// + /// (Optional; PDF 1.5) A permissions dictionary that specifies user access permissions + /// for the document. + /// + [KeyInfo("1.5", KeyType.Dictionary | KeyType.Optional)] + public const string Perms = "/Perms"; + + /// + /// (Optional; PDF 1.5) A dictionary containing attestations regarding the content of a + /// PDF document, as it relates to the legality of digital signatures. + /// + [KeyInfo("1.5", KeyType.Dictionary | KeyType.Optional)] + public const string Legal = "/Legal"; + + /// + /// (Optional; PDF 1.7) An array of requirement dictionaries representing + /// requirements for the document. + /// + [KeyInfo("1.7", KeyType.Array | KeyType.Optional)] + public const string Requirements = "/Requirements"; + + /// + /// (Optional; PDF 1.7) A collection dictionary that a PDF consumer uses to enhance + /// the presentation of file attachments stored in the PDF document. + /// + [KeyInfo("1.7", KeyType.Dictionary | KeyType.Optional)] + public const string Collection = "/Collection"; + + /// + /// (Optional; PDF 1.7) A flag used to expedite the display of PDF documents containing XFA forms. + /// It specifies whether the document must be regenerated when the document is first opened. + /// If true, the viewer application treats the document as a shell and regenerates the content + /// when the document is opened, regardless of any dynamic forms settings that appear in the XFA + /// stream itself. This setting is used to expedite the display of documents whose layout varies + /// depending on the content of the XFA streams. + /// If false, the viewer application does not regenerate the content when the document is opened. + /// See the XML Forms Architecture (XFA) Specification (Bibliography). + /// Default value: false. + /// + [KeyInfo("1.7", KeyType.Boolean | KeyType.Optional)] + public const string NeedsRendering = "/NeedsRendering"; + + // ReSharper restore InconsistentNaming + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContent.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContent.cs new file mode 100644 index 00000000..2e315f30 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContent.cs @@ -0,0 +1,192 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Drawing.Pdf; +using PdfSharp.Pdf.Filters; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents the content of a page. PDFsharp supports only one content stream per page. + /// If an imported page has an array of content streams, the streams are concatenated to + /// one single stream. + /// + public sealed class PdfContent : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfContent(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. + /// + internal PdfContent(PdfPage page) + : base(page != null ? page.Owner : null) + { + //_pageContent = new PageContent(page); + } + + /// + /// Initializes a new instance of the class. + /// + /// The dict. + public PdfContent(PdfDictionary dict) // HACK PdfContent + : base(dict) + { + // A PdfContent dictionary is always unfiltered. + Decode(); + } + + /// + /// Sets a value indicating whether the content is compressed with the ZIP algorithm. + /// + public bool Compressed + { + set + { + if (value) + { + PdfItem filter = Elements["/Filter"]; + if (filter == null) + { + byte[] bytes = Filtering.FlateDecode.Encode(Stream.Value, _document.Options.FlateEncodeMode); + Stream.Value = bytes; + Elements.SetInteger("/Length", Stream.Length); + Elements.SetName("/Filter", "/FlateDecode"); + } + } + } + } + + /// + /// Unfilters the stream. + /// + void Decode() + { + if (Stream != null && Stream.Value != null) + { + PdfItem item = Elements["/Filter"]; + if (item != null) + { + byte[] bytes = Filtering.Decode(Stream.Value, item); + if (bytes != null) + { + Stream.Value = bytes; + Elements.Remove("/Filter"); + Elements.SetInteger("/Length", Stream.Length); + } + } + } + } + + /// + /// Surround content with q/Q operations if necessary. + /// + internal void PreserveGraphicsState() + { + // If a content stream is touched by PDFsharp it is typically because graphical operations are + // prepended or appended. Some nasty PDF tools does not preserve the graphical state correctly. + // Therefore we try to relieve the problem by surrounding the content stream with push/restore + // graphic state operation. + if (Stream != null) + { + byte[] value = Stream.Value; + int length = value.Length; + if (length != 0 && ((value[0] != (byte)'q' || value[1] != (byte)'\n'))) + { + byte[] newValue = new byte[length + 2 + 3]; + newValue[0] = (byte)'q'; + newValue[1] = (byte)'\n'; + Array.Copy(value, 0, newValue, 2, length); + newValue[length + 2] = (byte)' '; + newValue[length + 3] = (byte)'Q'; + newValue[length + 4] = (byte)'\n'; + Stream.Value = newValue; + Elements.SetInteger("/Length", Stream.Length); + } + } + } + + internal override void WriteObject(PdfWriter writer) + { + if (_pdfRenderer != null) + { + // GetContent also disposes the underlying XGraphics object, if one exists + //Stream = new PdfStream(PdfEncoders.RawEncoding.GetBytes(pdfRenderer.GetContent()), this); + _pdfRenderer.Close(); + Debug.Assert(_pdfRenderer == null); + } + + if (Stream != null) + { + //if (Owner.Options.CompressContentStreams) + if (Owner.Options.CompressContentStreams && Elements.GetName("/Filter").Length == 0) + { + Stream.Value = Filtering.FlateDecode.Encode(Stream.Value, _document.Options.FlateEncodeMode); + //Elements["/Filter"] = new PdfName("/FlateDecode"); + Elements.SetName("/Filter", "/FlateDecode"); + } + Elements.SetInteger("/Length", Stream.Length); + } + + base.WriteObject(writer); + } + + internal XGraphicsPdfRenderer _pdfRenderer; + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : PdfStream.Keys + { + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContents.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContents.cs new file mode 100644 index 00000000..fb9fa727 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfContents.cs @@ -0,0 +1,267 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Collections; +using PdfSharp.Pdf.Content.Objects; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an array of PDF content streams of a page. + /// + public sealed class PdfContents : PdfArray + { + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfContents(PdfDocument document) + : base(document) + { } + + internal PdfContents(PdfArray array) + : base(array) + { + int count = Elements.Count; + for (int idx = 0; idx < count; idx++) + { + // Convert the references from PdfDictionary to PdfContent + PdfItem item = Elements[idx]; + PdfReference iref = item as PdfReference; + if (iref != null && iref.Value is PdfDictionary) + { + // The following line is correct! + new PdfContent((PdfDictionary)iref.Value); + } + else + throw new InvalidOperationException("Unexpected item in a content stream array."); + } + } + + /// + /// Appends a new content stream and returns it. + /// + public PdfContent AppendContent() + { + Debug.Assert(Owner != null); + + SetModified(); + PdfContent content = new PdfContent(Owner); + Owner._irefTable.Add(content); + Debug.Assert(content.Reference != null); + Elements.Add(content.Reference); + return content; + } + + /// + /// Prepends a new content stream and returns it. + /// + public PdfContent PrependContent() + { + Debug.Assert(Owner != null); + + SetModified(); + PdfContent content = new PdfContent(Owner); + Owner._irefTable.Add(content); + Debug.Assert(content.Reference != null); + Elements.Insert(0, content.Reference); + return content; + } + + /// + /// Creates a single content stream with the bytes from the array of the content streams. + /// This operation does not modify any of the content streams in this array. + /// + public PdfContent CreateSingleContent() + { + byte[] bytes = new byte[0]; + byte[] bytes1; + byte[] bytes2; + foreach (PdfItem iref in Elements) + { + PdfDictionary cont = (PdfDictionary)((PdfReference)iref).Value; + bytes1 = bytes; + bytes2 = cont.Stream.UnfilteredValue; + bytes = new byte[bytes1.Length + bytes2.Length + 1]; + bytes1.CopyTo(bytes, 0); + bytes[bytes1.Length] = (byte)'\n'; + bytes2.CopyTo(bytes, bytes1.Length + 1); + } + PdfContent content = new PdfContent(Owner); + content.Stream = new PdfDictionary.PdfStream(bytes, content); + return content; + } + + /// + /// Replaces the current content of the page with the specified content sequence. + /// + public PdfContent ReplaceContent(CSequence cseq) + { + if (cseq == null) + throw new ArgumentNullException(nameof(cseq)); + + return ReplaceContent(cseq.ToContent()); + } + + /// + /// Replaces the current content of the page with the specified bytes. + /// + PdfContent ReplaceContent(byte[] contentBytes) + { + Debug.Assert(Owner != null); + + PdfContent content = new PdfContent(Owner); + + content.CreateStream(contentBytes); + + Owner._irefTable.Add(content); + Elements.Clear(); + Elements.Add(content.Reference); + + return content; + } + + void SetModified() + { + if (!_modified) + { + _modified = true; + int count = Elements.Count; + + if (count == 1) + { + PdfContent content = (PdfContent)((PdfReference)Elements[0]).Value; + content.PreserveGraphicsState(); + } + else if (count > 1) + { + // Surround content streams with q/Q operations + byte[] value; + int length; + PdfContent content = (PdfContent)((PdfReference)Elements[0]).Value; + if (content != null && content.Stream != null) + { + length = content.Stream.Length; + value = new byte[length + 2]; + value[0] = (byte)'q'; + value[1] = (byte)'\n'; + Array.Copy(content.Stream.Value, 0, value, 2, length); + content.Stream.Value = value; + content.Elements.SetInteger("/Length", length + 2); + } + content = (PdfContent)((PdfReference)Elements[count - 1]).Value; + if (content != null && content.Stream != null) + { + length = content.Stream.Length; + value = new byte[length + 3]; + Array.Copy(content.Stream.Value, 0, value, 0, length); + value[length] = (byte)' '; + value[length + 1] = (byte)'Q'; + value[length + 2] = (byte)'\n'; + content.Stream.Value = value; + content.Elements.SetInteger("/Length", length + 3); + } + } + } + } + bool _modified; + + internal override void WriteObject(PdfWriter writer) + { + // Save two bytes in PDF stream... + if (Elements.Count == 1) + Elements[0].WriteObject(writer); + else + base.WriteObject(writer); + } + + /// + /// Gets the enumerator. + /// + public new IEnumerator GetEnumerator() + { + return new PdfPageContentEnumerator(this); + } + + class PdfPageContentEnumerator : IEnumerator + { + internal PdfPageContentEnumerator(PdfContents list) + { + _contents = list; + _index = -1; + } + + public bool MoveNext() + { + if (_index < _contents.Elements.Count - 1) + { + _index++; + _currentElement = (PdfContent)((PdfReference)_contents.Elements[_index]).Value; + return true; + } + _index = _contents.Elements.Count; + return false; + } + + public void Reset() + { + _currentElement = null; + _index = -1; + } + + object IEnumerator.Current + { + get { return Current; } + } + + public PdfContent Current + { + get + { + if (_index == -1 || _index >= _contents.Elements.Count) + throw new InvalidOperationException(PSSR.ListEnumCurrentOutOfRange); + return _currentElement; + } + } + + public void Dispose() + { + // Nothing to do. + } + + PdfContent _currentElement; + int _index; + readonly PdfContents _contents; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceStream.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceStream.cs new file mode 100644 index 00000000..43b00e50 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceStream.cs @@ -0,0 +1,157 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; +using System.Diagnostics; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF cross-reference stream. + /// + internal sealed class PdfCrossReferenceStream : PdfTrailer // Reference: 3.4.7 Cross-Reference Streams / Page 106 + { + /// + /// Initializes a new instance of the class. + /// + public PdfCrossReferenceStream(PdfDocument document) + : base(document) + { +#if DEBUG && CORE + if (Internal.PdfDiagnostics.TraceXrefStreams) + { + Debug.WriteLine("PdfCrossReferenceStream created."); + } +#endif + } + + public readonly List Entries = new List(); + + public struct CrossReferenceStreamEntry + { + // Reference: TABLE 3.16 Entries in a cross-reference stream / Page 109 + + public uint Type; // 0, 1, or 2. + + public uint Field2; + + public uint Field3; + } + + /// + /// Predefined keys for cross-reference dictionaries. + /// + public new class Keys : PdfTrailer.Keys // Reference: TABLE 3.15 Additional entries specific to a cross-reference stream dictionary / Page 107 + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be XRef for a cross-reference stream. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "XRef")] + public const string Type = "/Type"; + + /// + /// (Required) The number one greater than the highest object number + /// used in this section or in any section for which this is an update. + /// It is equivalent to the Size entry in a trailer dictionary. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public new const string Size = "/Size"; + + /// + /// (Optional) An array containing a pair of integers for each subsection in this section. + /// The first integer is the first object number in the subsection; the second integer + /// is the number of entries in the subsection. + /// The array is sorted in ascending order by object number. Subsections cannot overlap; + /// an object number may have at most one entry in a section. + /// Default value: [0 Size]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Index = "/Index"; + + /// + /// (Present only if the file has more than one cross-reference stream; not meaningful in + /// hybrid-reference files) The byte offset from the beginning of the file to the beginning + /// of the previous cross-reference stream. This entry has the same function as the Prev + /// entry in the trailer dictionary. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public new const string Prev = "/Prev"; + + /// + /// (Required) An array of integers representing the size of the fields in a single + /// cross-reference entry. The table describes the types of entries and their fields. + /// For PDF 1.5, W always contains three integers; the value of each integer is the + /// number of bytes (in the decoded stream) of the corresponding field. For example, + /// [1 2 1] means that the fields are one byte, two bytes, and one byte, respectively. + /// + /// A value of zero for an element in the W array indicates that the corresponding field + /// is not present in the stream, and the default value is used, if there is one. If the + /// first element is zero, the type field is not present, and it defaults to type 1. + /// + /// The sum of the items is the total length of each entry; it can be used with the + /// Indexarray to determine the starting position of each subsection. + /// + /// Note: Different cross-reference streams in a PDF file may use different values for W. + /// + /// Entries in a cross-reference stream. + /// + /// TYPE FIELD DESCRIPTION + /// 0 1 The type of this entry, which must be 0. Type 0 entries define the linked list of free objects (corresponding to f entries in a cross-reference table). + /// 2 The object number of the next free object. + /// 3 The generation number to use if this object number is used again. + /// 1 1 The type of this entry, which must be 1. Type 1 entries define objects that are in use but are not compressed (corresponding to n entries in a cross-reference table). + /// 2 The byte offset of the object, starting from the beginning of the file. + /// 3 The generation number of the object. Default value: 0. + /// 2 1 The type of this entry, which must be 2. Type 2 entries define compressed objects. + /// 2 The object number of the object stream in which this object is stored. (The generation number of the object stream is implicitly 0.) + /// 3 The index of this object within the object stream. + /// + [KeyInfo(KeyType.Array | KeyType.Required)] + public const string W = "/W"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static new DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceTable.cs new file mode 100644 index 00000000..89f5846c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfCrossReferenceTable.cs @@ -0,0 +1,590 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents the cross-reference table of a PDF document. + /// It contains all indirect objects of a document. + /// + internal sealed class PdfCrossReferenceTable // Must not be derive from PdfObject. + { + public PdfCrossReferenceTable(PdfDocument document) + { + _document = document; + } + readonly PdfDocument _document; + + /// + /// Represents the relation between PdfObjectID and PdfReference for a PdfDocument. + /// + public Dictionary ObjectTable = new Dictionary(); + + internal bool IsUnderConstruction + { + get { return _isUnderConstruction; } + set { _isUnderConstruction = value; } + } + bool _isUnderConstruction; + + /// + /// Adds a cross reference entry to the table. Used when parsing the trailer. + /// + public void Add(PdfReference iref) + { +#if DEBUG + if (iref.ObjectID.ObjectNumber == 948) + GetType(); +#endif + if (iref.ObjectID.IsEmpty) + iref.ObjectID = new PdfObjectID(GetNewObjectNumber()); + + if (ObjectTable.ContainsKey(iref.ObjectID)) + throw new InvalidOperationException("Object already in table."); + + ObjectTable.Add(iref.ObjectID, iref); + } + + /// + /// Adds a PdfObject to the table. + /// + public void Add(PdfObject value) + { + if (value.Owner == null) + value.Document = _document; + else + Debug.Assert(value.Owner == _document); + + if (value.ObjectID.IsEmpty) + value.SetObjectID(GetNewObjectNumber(), 0); + + if (ObjectTable.ContainsKey(value.ObjectID)) + throw new InvalidOperationException("Object already in table."); + + ObjectTable.Add(value.ObjectID, value.Reference); + } + + public void Remove(PdfReference iref) + { + ObjectTable.Remove(iref.ObjectID); + } + + /// + /// Gets a cross reference entry from an object identifier. + /// Returns null if no object with the specified ID exists in the object table. + /// + public PdfReference this[PdfObjectID objectID] + { + get + { + PdfReference iref; + ObjectTable.TryGetValue(objectID, out iref); + return iref; + } + } + + /// + /// Indicates whether the specified object identifier is in the table. + /// + public bool Contains(PdfObjectID objectID) + { + return ObjectTable.ContainsKey(objectID); + } + + //public PdfObject GetObject(PdfObjectID objectID) + //{ + // return this[objectID].Value; + //} + + // /// + // /// Gets the entry for the specified object, or null, if the object is not in + // /// this XRef table. + // /// + // internal PdfReference GetEntry(PdfObjectID objectID) + // { + // return this[objectID]; + // } + + /// + /// Returns the next free object number. + /// + public int GetNewObjectNumber() + { + // New objects are numbered consecutively. If a document is imported, maxObjectNumber is + // set to the highest object number used in the document. + return ++_maxObjectNumber; + } + internal int _maxObjectNumber; + + /// + /// Writes the xref section in pdf stream. + /// + internal void WriteObject(PdfWriter writer) + { + writer.WriteRaw("xref\n"); + + PdfReference[] irefs = AllReferences; + + int count = irefs.Length; + writer.WriteRaw(String.Format("0 {0}\n", count + 1)); + writer.WriteRaw(String.Format("{0:0000000000} {1:00000} {2} \n", 0, 65535, "f")); + //PdfEncoders.WriteAnsi(stream, text); + + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; + + // Acrobat is very pedantic; it must be exactly 20 bytes per line. + writer.WriteRaw(String.Format("{0:0000000000} {1:00000} {2} \n", iref.Position, iref.GenerationNumber, "n")); + } + } + + /// + /// Gets an array of all object identifiers. For debugging purposes only. + /// + internal PdfObjectID[] AllObjectIDs + { + get + { + ICollection collection = ObjectTable.Keys; + PdfObjectID[] objectIDs = new PdfObjectID[collection.Count]; + collection.CopyTo(objectIDs, 0); + return objectIDs; + } + } + + /// + /// Gets an array of all cross references in ascending order by their object identifier. + /// + internal PdfReference[] AllReferences + { + get + { + Dictionary.ValueCollection collection = ObjectTable.Values; + List list = new List(collection); + list.Sort(PdfReference.Comparer); + PdfReference[] irefs = new PdfReference[collection.Count]; + list.CopyTo(irefs, 0); + return irefs; + } + } + + internal void HandleOrphanedReferences() + { } + + /// + /// Removes all objects that cannot be reached from the trailer. + /// Returns the number of removed objects. + /// + internal int Compact() + { + // TODO: remove PdfBooleanObject, PdfIntegerObject etc. + int removed = ObjectTable.Count; + //CheckConsistence(); + // TODO: Is this really so easy? + PdfReference[] irefs = TransitiveClosure(_document._trailer); + +#if DEBUG + // Have any two objects the same ID? + Dictionary ids = new Dictionary(); + foreach (PdfObjectID objID in ObjectTable.Keys) + { + ids.Add(objID.ObjectNumber, 0); + } + + // Have any two irefs the same value? + //Dictionary ids = new Dictionary(); + ids.Clear(); + foreach (PdfReference iref in ObjectTable.Values) + { + ids.Add(iref.ObjectNumber, 0); + } + + // + Dictionary refs = new Dictionary(); + foreach (PdfReference iref in irefs) + { + refs.Add(iref, 0); + } + foreach (PdfReference value in ObjectTable.Values) + { + if (!refs.ContainsKey(value)) + value.GetType(); + } + + foreach (PdfReference iref in ObjectTable.Values) + { + if (iref.Value == null) + GetType(); + Debug.Assert(iref.Value != null); + } + + foreach (PdfReference iref in irefs) + { + if (!ObjectTable.ContainsKey(iref.ObjectID)) + GetType(); + Debug.Assert(ObjectTable.ContainsKey(iref.ObjectID)); + + if (iref.Value == null) + GetType(); + Debug.Assert(iref.Value != null); + } +#endif + + _maxObjectNumber = 0; + ObjectTable.Clear(); + foreach (PdfReference iref in irefs) + { + // This if is needed for corrupt PDF files from the wild. + // Without the if, an exception will be thrown if the file contains duplicate IDs ("An item with the same key has already been added to the dictionary."). + // With the if, the first object with the ID will be used and later objects with the same ID will be ignored. + if (!ObjectTable.ContainsKey(iref.ObjectID)) + { + ObjectTable.Add(iref.ObjectID, iref); + _maxObjectNumber = Math.Max(_maxObjectNumber, iref.ObjectNumber); + } + } + //CheckConsistence(); + removed -= ObjectTable.Count; + return removed; + } + + /// + /// Renumbers the objects starting at 1. + /// + internal void Renumber() + { + //CheckConsistence(); + PdfReference[] irefs = AllReferences; + ObjectTable.Clear(); + // Give all objects a new number. + int count = irefs.Length; + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; +#if DEBUG_ + if (iref.ObjectNumber == 1108) + GetType(); +#endif + iref.ObjectID = new PdfObjectID(idx + 1); + // Rehash with new number. + ObjectTable.Add(iref.ObjectID, iref); + } + _maxObjectNumber = count; + //CheckConsistence(); + } + + /// + /// Checks the logical consistence for debugging purposes (useful after reconstruction work). + /// + [Conditional("DEBUG_")] + public void CheckConsistence() + { + Dictionary ht1 = new Dictionary(); + foreach (PdfReference iref in ObjectTable.Values) + { + Debug.Assert(!ht1.ContainsKey(iref), "Duplicate iref."); + Debug.Assert(iref.Value != null); + ht1.Add(iref, null); + } + + Dictionary ht2 = new Dictionary(); + foreach (PdfReference iref in ObjectTable.Values) + { + Debug.Assert(!ht2.ContainsKey(iref.ObjectID), "Duplicate iref."); + ht2.Add(iref.ObjectID, null); + } + + ICollection collection = ObjectTable.Values; + int count = collection.Count; + PdfReference[] irefs = new PdfReference[count]; + collection.CopyTo(irefs, 0); +#if true + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) + if (i != j) + { + Debug.Assert(ReferenceEquals(irefs[i].Document, _document)); + Debug.Assert(irefs[i] != irefs[j]); + Debug.Assert(!ReferenceEquals(irefs[i], irefs[j])); + Debug.Assert(!ReferenceEquals(irefs[i].Value, irefs[j].Value)); + Debug.Assert(!Equals(irefs[i].ObjectID, irefs[j].Value.ObjectID)); + Debug.Assert(irefs[i].ObjectNumber != irefs[j].Value.ObjectNumber); + Debug.Assert(ReferenceEquals(irefs[i].Document, irefs[j].Document)); + GetType(); + } +#endif + } + + ///// + ///// The garbage collector for PDF objects. + ///// + //public sealed class GC + //{ + // PdfXRefTable xrefTable; + // + // internal GC(PdfXRefTable xrefTable) + // { + // _xrefTable = xrefTable; + // } + // + // public void Collect() + // { } + // + // public PdfReference[] ReachableObjects() + // { + // Hash_table objects = new Hash_table(); + // TransitiveClosure(objects, _xrefTable.document.trailer); + // } + + /// + /// Calculates the transitive closure of the specified PdfObject, i.e. all indirect objects + /// recursively reachable from the specified object. + /// + public PdfReference[] TransitiveClosure(PdfObject pdfObject) + { + return TransitiveClosure(pdfObject, short.MaxValue); + } + + /// + /// Calculates the transitive closure of the specified PdfObject with the specified depth, i.e. all indirect objects + /// recursively reachable from the specified object in up to maximally depth steps. + /// + public PdfReference[] TransitiveClosure(PdfObject pdfObject, int depth) + { + CheckConsistence(); + Dictionary objects = new Dictionary(); + _overflow = new Dictionary(); + TransitiveClosureImplementation(objects, pdfObject); + TryAgain: + if (_overflow.Count > 0) + { + PdfObject[] array = new PdfObject[_overflow.Count]; + _overflow.Keys.CopyTo(array, 0); + _overflow = new Dictionary(); + for (int idx = 0; idx < array.Length; idx++) + { + PdfObject obj = array[idx]; + TransitiveClosureImplementation(objects, obj); + } + goto TryAgain; + } + + CheckConsistence(); + + ICollection collection = objects.Keys; + int count = collection.Count; + PdfReference[] irefs = new PdfReference[count]; + collection.CopyTo(irefs, 0); + +#if true_ + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) + if (i != j) + { + Debug.Assert(ReferenceEquals(irefs[i].Document, _document)); + Debug.Assert(irefs[i] != irefs[j]); + Debug.Assert(!ReferenceEquals(irefs[i], irefs[j])); + Debug.Assert(!ReferenceEquals(irefs[i].Value, irefs[j].Value)); + Debug.Assert(!Equals(irefs[i].ObjectID, irefs[j].Value.ObjectID)); + Debug.Assert(irefs[i].ObjectNumber != irefs[j].Value.ObjectNumber); + Debug.Assert(ReferenceEquals(irefs[i].Document, irefs[j].Document)); + GetType(); + } +#endif + return irefs; + } + + static int _nestingLevel; + Dictionary _overflow = new Dictionary(); + + void TransitiveClosureImplementation(Dictionary objects, PdfObject pdfObject/*, ref int depth*/) + { + try + { + _nestingLevel++; + if (_nestingLevel >= 1000) + { + if (!_overflow.ContainsKey(pdfObject)) + _overflow.Add(pdfObject, null); + return; + } +#if DEBUG_ + //enterCount++; + if (enterCount == 5400) + GetType(); + //if (!Object.ReferenceEquals(pdfObject.Owner, _document)) + // GetType(); + //////Debug.Assert(Object.ReferenceEquals(pdfObject27.Document, _document)); + // if (item is PdfObject && ((PdfObject)item).ObjectID.ObjectNumber == 5) + // Debug.WriteLine("items: " + ((PdfObject)item).ObjectID.ToString()); + //if (pdfObject.ObjectNumber == 5) + // GetType(); +#endif + + IEnumerable enumerable = null; //(IEnumerator)pdfObject; + PdfDictionary dict; + PdfArray array; + if ((dict = pdfObject as PdfDictionary) != null) + enumerable = dict.Elements.Values; + else if ((array = pdfObject as PdfArray) != null) + enumerable = array.Elements; + else + Debug.Assert(false, "Should not come here."); + + if (enumerable != null) + { + foreach (PdfItem item in enumerable) + { + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Is this an indirect reference to an object that does not exist? + //if (iref.Document == null) + //{ + // Debug.WriteLine("Dead object detected: " + iref.ObjectID.ToString()); + // PdfReference dead = DeadObject; + // iref.ObjectID = dead.ObjectID; + // iref.Document = _document; + // iref.SetObject(dead.Value); + // PdfDictionary dict = (PdfDictionary)dead.Value; + + // dict.Elements["/DeadObjectCount"] = + // new PdfInteger(dict.Elements.GetInteger("/DeadObjectCount") + 1); + + // iref = dead; + //} + + if (!ReferenceEquals(iref.Document, _document)) + { + GetType(); + Debug.WriteLine(String.Format("Bad iref: {0}", iref.ObjectID.ToString())); + } + Debug.Assert(ReferenceEquals(iref.Document, _document) || iref.Document == null, "External object detected!"); +#if DEBUG_ + if (iref.ObjectID.ObjectNumber == 23) + GetType(); +#endif + if (!objects.ContainsKey(iref)) + { + PdfObject value = iref.Value; + + // Ignore unreachable objects. + if (iref.Document != null) + { + // ... from trailer hack + if (value == null) + { + iref = ObjectTable[iref.ObjectID]; + Debug.Assert(iref.Value != null); + value = iref.Value; + } + Debug.Assert(ReferenceEquals(iref.Document, _document)); + objects.Add(iref, null); + //Debug.WriteLine(String.Format("objects.Add('{0}', null);", iref.ObjectID.ToString())); + if (value is PdfArray || value is PdfDictionary) + TransitiveClosureImplementation(objects, value /*, ref depth*/); + } + //else + //{ + // objects2.Add(this[iref.ObjectID], null); + //} + } + } + else + { + PdfObject pdfObject28 = item as PdfObject; + //if (pdfObject28 != null) + // Debug.Assert(Object.ReferenceEquals(pdfObject28.Document, _document)); + if (pdfObject28 != null && (pdfObject28 is PdfDictionary || pdfObject28 is PdfArray)) + TransitiveClosureImplementation(objects, pdfObject28 /*, ref depth*/); + } + } + } + } + finally + { + _nestingLevel--; + } + } + + /// + /// Gets the cross reference to an objects used for undefined indirect references. + /// + public PdfReference DeadObject + { + get + { + if (_deadObject == null) + { + _deadObject = new PdfDictionary(_document); + Add(_deadObject); + _deadObject.Elements.Add("/DeadObjectCount", new PdfInteger()); + } + return _deadObject.Reference; + } + } + PdfDictionary _deadObject; + } + + ///// + ///// Represents the cross-reference table of a PDF document. + ///// It contains all indirect objects of a document. + ///// + //internal sealed class PdfCrossReferenceStreamTable // Must not be derive from PdfObject. + //{ + // public PdfCrossReferenceStreamTable(PdfDocument document) + // { + // _document = document; + // } + // readonly PdfDocument _document; + + // public class Item + // { + // public PdfReference Reference; + + // public readonly List Entries = new List(); + // } + //} + + //struct CrossReferenceStreamEntry + //{ + // public int Type; + + // public int Field2; + + // public int Field3; + //} +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfDictionaryWithContentStream.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfDictionaryWithContentStream.cs new file mode 100644 index 00000000..e2173b5f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfDictionaryWithContentStream.cs @@ -0,0 +1,169 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a base class for dictionaries with a content stream. + /// Implement IContentStream for use with a content writer. + /// + public abstract class PdfDictionaryWithContentStream : PdfDictionary, IContentStream + { + /// + /// Initializes a new instance of the class. + /// + public PdfDictionaryWithContentStream() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfDictionaryWithContentStream(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance from an existing dictionary. Used for object type transformation. + /// + protected PdfDictionaryWithContentStream(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets the resources dictionary of this dictionary. If no such dictionary exists, it is created. + /// + internal PdfResources Resources + { + get + { + if (_resources == null) + _resources = (PdfResources)Elements.GetValue(Keys.Resources, VCF.Create); + return _resources; + } + } + PdfResources _resources; + + /// + /// Implements the interface because the primary function is internal. + /// + PdfResources IContentStream.Resources + { + get { return Resources; } + } + + internal string GetFontName(XFont font, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(font); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(XFont font, out PdfFont pdfFont) + { + return GetFontName(font, out pdfFont); + } + + internal string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(idName, fontData); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + return GetFontName(idName, fontData, out pdfFont); + } + + /// + /// Gets the resource name of the specified image within this dictionary. + /// + internal string GetImageName(XImage image) + { + PdfImage pdfImage = _document.ImageTable.GetImage(image); + Debug.Assert(pdfImage != null); + string name = Resources.AddImage(pdfImage); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetImageName(XImage image) + { + throw new NotImplementedException(); + } + + /// + /// Gets the resource name of the specified form within this dictionary. + /// + internal string GetFormName(XForm form) + { + PdfFormXObject pdfForm = _document.FormTable.GetForm(form); + Debug.Assert(pdfForm != null); + string name = Resources.AddForm(pdfForm); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetFormName(XForm form) + { + throw new NotImplementedException(); + } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : PdfDictionary.PdfStream.Keys + { + /// + /// (Optional but strongly recommended; PDF 1.2) A dictionary specifying any + /// resources (such as fonts and images) required by the form XObject. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResources))] + public const string Resources = "/Resources"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfEmbeddedFileStream.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfEmbeddedFileStream.cs new file mode 100644 index 00000000..8e97a184 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfEmbeddedFileStream.cs @@ -0,0 +1,65 @@ +using System.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an embedded file stream. + /// PDF 1.3. + /// + public class PdfEmbeddedFileStream : PdfDictionary + { + /// + /// Initializes a new instance of PdfEmbeddedFileStream from a stream. + /// + public PdfEmbeddedFileStream(PdfDocument document, Stream stream) : base(document) + { + _data = new byte[stream.Length]; + using (stream) + { + stream.Read(_data, 0, (int)stream.Length); + } + + Initialize(); + } + + private void Initialize() + { + Elements.SetName(Keys.Type, "/EmbeddedFile"); + Elements.SetInteger(PdfStream.Keys.Length, _data.Length); + + Stream = new PdfStream(_data, this); + } + + private readonly byte[] _data; + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : PdfStream.Keys + { + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be EmbeddedFile for an embedded file stream. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Optional) The subtype of the embedded file. The value of this entry must be a first-class name, + /// as defined in Appendix E. Names without a registered prefix must conform to the MIME media type names + /// defined in Internet RFC 2046, Multipurpose Internet Mail Extensions (MIME), Part Two: Media Types + /// (see the Bibliography), with the provision that characters not allowed in names must use the + /// 2-character hexadecimal code format described in Section 3.2.4, “Name Objects.” + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Subtype = "/Subtype"; + + /// + /// (Optional) An embedded file parameter dictionary containing additional, + /// file-specific information (see Table 3.43). + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Params = "/Params"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGState.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGState.cs new file mode 100644 index 00000000..3c3fd744 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGState.cs @@ -0,0 +1,411 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Globalization; + +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +#endif + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an extended graphics state object. + /// + public sealed class PdfExtGState : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfExtGState(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/ExtGState"); + +#if true_ + //AIS false + //BM /Normal + //ca 1 + //CA 1 + //op false + //OP false + //OPM 1 + //SA true + //SMask /None + //Type /ExtGState + + Elements.SetValue(Keys.AIS, new PdfBoolean(false)); // The alpha source + Elements.SetName("/BM", "Normal"); + Elements.SetValue(Keys.op, new PdfBoolean(false)); + Elements.SetValue(Keys.OP, new PdfBoolean(false)); + Elements.SetValue(Keys.OPM, new PdfInteger(1)); + Elements.SetValue("/SA", new PdfBoolean(true)); + Elements.SetName("/SMask", "None"); +#endif + //#if OP_HACK + // Elements.SetValue(Keys.op, new PdfBoolean(false)); + // Elements.SetValue(Keys.OP, new PdfBoolean(false)); + // Elements.SetValue(Keys.OPM, new PdfInteger(1)); + //#endif + } + + /// + /// Used in Edf.Xps. + /// + internal void SetDefault1() + { + //<< + // /AIS false + // /BM /Normal + // /ca 1 + // /CA 1 + // /op false + // /OP false + // /OPM 1 + // /SA true + // /SMask /None + // /Type /ExtGState + //>> + Elements.SetBoolean(Keys.AIS, false); + if (Elements.ContainsKey(Keys.BM)) Elements.SetName(Keys.BM, "/Normal"); + StrokeAlpha = 1; + NonStrokeAlpha = 1; + Elements.SetBoolean(Keys.op, false); + Elements.SetBoolean(Keys.OP, false); + Elements.SetBoolean(Keys.SA, true); + Elements.SetName(Keys.SMask, "/None"); + } + + /// + /// Used in Edf.Xps. + /// ...for shading patterns + /// + internal void SetDefault2() + { + //<< + // /AIS false + // /BM /Normal + // /ca 1 + // /CA 1 + // /op true + // /OP true + // /OPM 1 + // /SA true + // /SMask /None + // /Type /ExtGState + //>> + Elements.SetBoolean(Keys.AIS, false); + Elements.SetName(Keys.BM, "/Normal"); + StrokeAlpha = 1; + NonStrokeAlpha = 1; + Elements.SetBoolean(Keys.op, true); + Elements.SetBoolean(Keys.OP, true); + Elements.SetInteger(Keys.OPM, 1); + Elements.SetBoolean(Keys.SA, true); + Elements.SetName(Keys.SMask, "/None"); + } + + /// + /// Sets the alpha value for stroking operations. + /// + public double StrokeAlpha + { + set + { + _strokeAlpha = value; + Elements.SetReal(Keys.CA, value); + UpdateKey(); + } + } + double _strokeAlpha; + + /// + /// Sets the alpha value for nonstroking operations. + /// + public double NonStrokeAlpha + { + set + { + _nonStrokeAlpha = value; + Elements.SetReal(Keys.ca, value); + UpdateKey(); + } + } + double _nonStrokeAlpha; + + /// + /// Sets the overprint value for stroking operations. + /// + public bool StrokeOverprint + { + set + { + _strokeOverprint = value; + Elements.SetBoolean(Keys.OP, value); + UpdateKey(); + } + } + bool _strokeOverprint; + + /// + /// Sets the overprint value for nonstroking operations. + /// + public bool NonStrokeOverprint + { + set + { + _nonStrokeOverprint = value; + Elements.SetBoolean(Keys.op, value); + UpdateKey(); + } + } + bool _nonStrokeOverprint; + + /// + /// Sets a soft mask object. + /// + public PdfSoftMask SoftMask + { + set { Elements.SetReference(Keys.SMask, value); } + } + + internal string Key + { + get { return _key; } + } + + void UpdateKey() + { + _key = ((int)(1000 * _strokeAlpha)).ToString(CultureInfo.InvariantCulture) + + ((int)(1000 * _nonStrokeAlpha)).ToString(CultureInfo.InvariantCulture) + + (_strokeOverprint ? "S" : "s") + (_nonStrokeOverprint ? "N" : "n"); + } + string _key; + + internal static string MakeKey(double alpha, bool overPaint) + { + string key = ((int)(1000 * alpha)).ToString(CultureInfo.InvariantCulture) + (overPaint ? "O" : "0"); + return key; + } + + /// + /// Common keys for all streams. + /// + internal sealed class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Optional) The type of PDF object that this dictionary describes; + /// must be ExtGState for a graphics state parameter dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Optional; PDF 1.3) The line width (see Line Width on page 185). + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string LW = "/LW"; + + /// + /// (Optional; PDF 1.3) The line cap style. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string LC = "/LC"; + + /// + /// (Optional; PDF 1.3) The line join style. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string LJ = "/LJ"; + + /// + /// (Optional; PDF 1.3) The miter limit. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string ML = "/ML"; + + /// + /// (Optional; PDF 1.3) The line dash pattern, expressed as an array of the form + /// [dashArray dashPhase], where dashArray is itself an array and dashPhase is an integer. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string D = "/D"; + + /// + /// (Optional; PDF 1.3) The name of the rendering intent. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string RI = "/RI"; + + /// + /// (Optional) A flag specifying whether to apply overprint. In PDF 1.2 and earlier, + /// there is a single overprint parameter that applies to all painting operations. + /// Beginning with PDF 1.3, there are two separate overprint parameters: one for stroking + /// and one for all other painting operations. Specifying an OP entry sets both parameters + /// unless there is also an op entry in the same graphics state parameter dictionary, in + /// which case the OP entry sets only the overprint parameter for stroking. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string OP = "/OP"; + + /// + /// (Optional; PDF 1.3) A flag specifying whether to apply overprint for painting operations + /// other than stroking. If this entry is absent, the OP entry, if any, sets this parameter. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string op = "/op"; + + /// + /// (Optional; PDF 1.3) The overprint mode. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string OPM = "/OPM"; + + /// + /// (Optional; PDF 1.3) An array of the form [font size], where font is an indirect + /// reference to a font dictionary and size is a number expressed in text space units. + /// These two objects correspond to the operands of the Tf operator; however, + /// the first operand is an indirect object reference instead of a resource name. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Font = "/Font"; + + /// + /// (Optional) The black-generation function, which maps the interval [0.0 1.0] + /// to the interval [0.0 1.0]. + /// + [KeyInfo(KeyType.Function | KeyType.Optional)] + public const string BG = "/BG"; + + /// + /// (Optional; PDF 1.3) Same as BG except that the value may also be the name Default, + /// denoting the black-generation function that was in effect at the start of the page. + /// If both BG and BG2 are present in the same graphics state parameter dictionary, + /// BG2 takes precedence. + /// + [KeyInfo(KeyType.FunctionOrName | KeyType.Optional)] + public const string BG2 = "/BG2"; + + /// + /// (Optional) The undercolor-removal function, which maps the interval + /// [0.0 1.0] to the interval [-1.0 1.0]. + /// + [KeyInfo(KeyType.Function | KeyType.Optional)] + public const string UCR = "/UCR"; + + /// + /// (Optional; PDF 1.3) Same as UCR except that the value may also be the name Default, + /// denoting the undercolor-removal function that was in effect at the start of the page. + /// If both UCR and UCR2 are present in the same graphics state parameter dictionary, + /// UCR2 takes precedence. + /// + [KeyInfo(KeyType.FunctionOrName | KeyType.Optional)] + public const string UCR2 = "/UCR2"; + + //TR function, array, or name + //TR2 function, array, or name + //HT dictionary, stream, or name + //FL number + //SM number + + /// + /// (Optional) A flag specifying whether to apply automatic stroke adjustment. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string SA = "/SA"; + + /// + /// (Optional; PDF 1.4) The current blend mode to be used in the transparent imaging model. + /// + [KeyInfo(KeyType.NameOrArray | KeyType.Optional)] + public const string BM = "/BM"; + + /// + /// (Optional; PDF 1.4) The current soft mask, specifying the mask shape or + /// mask opacity values to be used in the transparent imaging model. + /// + [KeyInfo(KeyType.NameOrDictionary | KeyType.Optional)] + public const string SMask = "/SMask"; + + /// + /// (Optional; PDF 1.4) The current stroking alpha constant, specifying the constant + /// shape or constant opacity value to be used for stroking operations in the transparent + /// imaging model. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string CA = "/CA"; + + /// + /// (Optional; PDF 1.4) Same as CA, but for nonstroking operations. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string ca = "/ca"; + + /// + /// (Optional; PDF 1.4) The alpha source flag (alpha is shape), specifying whether + /// the current soft mask and alpha constant are to be interpreted as shape values (true) + /// or opacity values (false). + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string AIS = "/AIS"; + + /// + /// (Optional; PDF 1.4) The text knockout flag, which determines the behavior of + /// overlapping glyphs within a text object in the transparent imaging model. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string TK = "/TK"; + + // ReSharper restore InconsistentNaming + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGStateTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGStateTable.cs new file mode 100644 index 00000000..05a1ffae --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfExtGStateTable.cs @@ -0,0 +1,95 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Contains all used ExtGState objects of a document. + /// + public sealed class PdfExtGStateTable : PdfResourceTable + { + /// + /// Initializes a new instance of this class, which is a singleton for each document. + /// + public PdfExtGStateTable(PdfDocument document) + : base(document) + { } + + + /// + /// Gets a PdfExtGState with the key 'CA' set to the specified alpha value. + /// + public PdfExtGState GetExtGStateStroke(double alpha, bool overprint) + { + string key = PdfExtGState.MakeKey(alpha, overprint); + PdfExtGState extGState; + if (!_strokeAlphaValues.TryGetValue(key, out extGState)) + { + extGState = new PdfExtGState(Owner); + //extGState.Elements[PdfExtGState.Keys.CA] = new PdfReal(alpha); + extGState.StrokeAlpha = alpha; + if (overprint) + { + extGState.StrokeOverprint = true; + extGState.Elements.SetInteger(PdfExtGState.Keys.OPM, 1); + } + _strokeAlphaValues[key] = extGState; + } + return extGState; + } + + /// + /// Gets a PdfExtGState with the key 'ca' set to the specified alpha value. + /// + public PdfExtGState GetExtGStateNonStroke(double alpha, bool overprint) + { + string key = PdfExtGState.MakeKey(alpha, overprint); + PdfExtGState extGState; + if (!_nonStrokeStates.TryGetValue(key, out extGState)) + { + extGState = new PdfExtGState(Owner); + //extGState.Elements[PdfExtGState.Keys.ca] = new PdfReal(alpha); + extGState.NonStrokeAlpha = alpha; + if (overprint) + { + extGState.NonStrokeOverprint = true; + extGState.Elements.SetInteger(PdfExtGState.Keys.OPM, 1); + } + + _nonStrokeStates[key] = extGState; + } + return extGState; + } + + readonly Dictionary _strokeAlphaValues = new Dictionary(); + readonly Dictionary _nonStrokeStates = new Dictionary(); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFileSpecification.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFileSpecification.cs new file mode 100644 index 00000000..c4cb9370 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFileSpecification.cs @@ -0,0 +1,172 @@ +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a file specification dictionary. + /// + public class PdfFileSpecification : PdfDictionary + { + /// + /// Initializes a new instance of PdfFileSpecification refering an embedded file stream. + /// + public PdfFileSpecification(PdfDocument document, PdfEmbeddedFileStream embeddedFileStream, string name) : base(document) + { + _embeddedFileStream = embeddedFileStream; + _name = name; + + Initialize(); + } + + private void Initialize() + { + Elements.SetName(Keys.Type, "/Filespec"); + + Elements.SetString(Keys.F, _name); + Elements.SetString(Keys.UF, _name); + + var embeddedFileDictionary = new PdfDictionary(Owner); + + Owner.Internals.AddObject(_embeddedFileStream); + embeddedFileDictionary.Elements.SetReference(Keys.F, _embeddedFileStream.Reference); + embeddedFileDictionary.Elements.SetReference(Keys.UF, _embeddedFileStream.Reference); + + Elements.SetObject(Keys.EF, embeddedFileDictionary); + } + private readonly PdfEmbeddedFileStream _embeddedFileStream; + private readonly string _name; + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Required if an EF or RF entry is present; recommended always) + /// The type of PDF object that this dictionary describes; must be Filespec + /// for a file specification dictionary (see implementation note 45 in Appendix H). + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + ///// + ///// (Optional) The name of the file system to be used to interpret this file specification. + ///// If this entry is present, all other entries in the dictionary are interpreted by the + ///// designated file system. PDF defines only one standard file system name, URL + ///// (see Section 3.10.4, “URL Specifications”); an application or plug-in extension can + ///// register other names (see Appendix E). This entry is independent of the F, UF, DOS, + ///// Mac, and Unixentries. + ///// + //[KeyInfo(KeyType.Name | KeyType.Optional)] + //public const string FS = "/FS"; + + /// + /// (Required if the DOS, Mac, and Unix entries are all absent; amended with the UF entry + /// for PDF 1.7) A file specification string of the form described in Section 3.10.1, + /// “File Specification Strings,” or (if the file system is URL) a uniform resource locator, + /// as described in Section 3.10.4, “URL Specifications.” + /// Note: It is recommended that the UF entry be used in addition to the F entry.The UF entry + /// provides cross-platform and cross-language compatibility and the F entry provides + /// backwards compatibility. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string F = "/F"; + + /// + /// (Optional, but recommended if the F entry exists in the dictionary; PDF 1.7) A Unicode + /// text string that provides file specification of the form described in Section 3.10.1, + /// “File Specification Strings.” Note that this is a Unicode text string encoded using + /// PDFDocEncoding or UTF-16BE with a leading byte-order marker (as defined in Section , + /// “Text String Type”). The F entry should always be included along with this entry for + /// backwards compatibility reasons. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string UF = "/UF"; + + ///// + ///// (Optional) A file specification string (see Section 3.10.1, “File Specification Strings”) + ///// representing a DOS file name. + ///// Note: Beginning with PDF 1.7, use of the F entry and optionally the UF entry is recommended + ///// in place of the DOS, Mac or Unix entries. + ///// + //[KeyInfo(KeyType.ByteString | KeyType.Optional)] + //public const string DOS = "/DOS"; + + ///// + ///// (Optional) A file specification string (see Section 3.10.1, “File Specification Strings”) + ///// representing a Mac OS file name. + ///// Note: Beginning with PDF 1.7, use of the F entry and optionally the UF entry is recommended + ///// in place of the DOS, Mac or Unix entries. + ///// + //[KeyInfo(KeyType.ByteString | KeyType.Optional)] + //public const string Mac = "/Mac"; + + ///// + ///// (Optional) A file specification string (see Section 3.10.1, “File Specification Strings”) + ///// representing a UNIX file name. + ///// Note: Beginning with PDF 1.7, use of the F entry and optionally the UF entry is recommended + ///// in place of the DOS, Mac or Unix entries. + ///// + //[KeyInfo(KeyType.ByteString | KeyType.Optional)] + //public const string Unix = "/Unix"; + + ///// + ///// (Optional) An array of two byte strings constituting a file identifier (see Section 10.3, + ///// “File Identifiers”) that is also included in the referenced file. The use of this entry + ///// improves an application’s chances of finding the intended file and allows it to warn the + ///// user if the file has changed since the link was made. + ///// + //[KeyInfo(KeyType.Array | KeyType.Optional)] + //public const string ID = "/ID"; + + ///// + ///// (Optional; PDF 1.2) A flag indicating whether the file referenced by the file specification + ///// is volatile (changes frequently with time). If the value is true, applications should never + ///// cache a copy of the file. For example, a movie annotation referencing a URL to a live video + ///// camera could set this flag to true to notify the application that it should reacquire the + ///// movie each time it is played. Default value: false. + ///// + //[KeyInfo(KeyType.Boolean | KeyType.Optional)] + //public const string V = "/V"; + + /// + /// (Required if RF is present; PDF 1.3; amended to include the UF key in PDF 1.7) A dictionary + /// containing a subset of the keys F, UF, DOS, Mac, and Unix, corresponding to the entries by + /// those names in the file specification dictionary. The value of each such key is an embedded + /// file stream (see Section 3.10.3, “Embedded File Streams”) containing the corresponding file. + /// If this entry is present, the Type entry is required and the file specification dictionary + /// must be indirectly referenced. (See implementation note 46in Appendix H.) + /// Note: It is recommended that the F and UF entries be used in place of the DOS, Mac, or Unix + /// entries. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string EF = "/EF"; + + ///// + ///// (Optional; PDF 1.3) A dictionary with the same structure as the EF dictionary, which must also + ///// be present. Each key in the RF dictionary must also be present in the EF dictionary. Each value + ///// is a related files array (see “Related Files Arrays” on page 186) identifying files that are + ///// related to the corresponding file in the EF dictionary. If this entry is present, the Type entry + ///// is required and the file specification dictionary must be indirectly referenced. + ///// + //[KeyInfo(KeyType.Dictionary | KeyType.Optional)] + //public const string RF = "/RF"; + + ///// + ///// (Optional; PDF 1.6) Descriptive text associated with the file specification. It is used for + ///// files in the EmbeddedFiles name tree (see Section 3.6.3, “Name Dictionary”). + ///// + //[KeyInfo(KeyType.TextString | KeyType.Optional)] + //public const string Desc = "/Desc"; + + ///// + ///// (Optional; must be indirect reference; PDF 1.7) A collection item dictionary, which is used to + ///// create the user interface for portable collections (see Section 3.10.5, “Collection Items). + ///// + //[KeyInfo(KeyType.Dictionary | KeyType.Optional)] + //public const string CI = "/CI"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFont.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFont.cs new file mode 100644 index 00000000..8e19782c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFont.cs @@ -0,0 +1,156 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Text; +using PdfSharp.Fonts; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF font. + /// + public class PdfFont : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfFont(PdfDocument document) + : base(document) + { } + + internal PdfFontDescriptor FontDescriptor + { + get + { + Debug.Assert(_fontDescriptor != null); + return _fontDescriptor; + } + set { _fontDescriptor = value; } + } + PdfFontDescriptor _fontDescriptor; + + internal PdfFontEncoding FontEncoding; + + /// + /// Gets a value indicating whether this instance is symbol font. + /// + public bool IsSymbolFont + { + get { return _fontDescriptor.IsSymbolFont; } + } + + internal void AddChars(string text) + { + if (_cmapInfo != null) + _cmapInfo.AddChars(text); + } + + internal void AddGlyphIndices(string glyphIndices) + { + if (_cmapInfo != null) + _cmapInfo.AddGlyphIndices(glyphIndices); + } + + /// + /// Gets or sets the CMapInfo. + /// + internal CMapInfo CMapInfo + { + get { return _cmapInfo; } + set { _cmapInfo = value; } + } + internal CMapInfo _cmapInfo; + + /// + /// Gets or sets ToUnicodeMap. + /// + internal PdfToUnicodeMap ToUnicodeMap + { + get { return _toUnicode; } + set { _toUnicode = value; } + } + internal PdfToUnicodeMap _toUnicode; + + + /// + /// Adds a tag of exactly six uppercase letters to the font name + /// according to PDF Reference Section 5.5.3 'Font Subsets' + /// + internal static string CreateEmbeddedFontSubsetName(string name) + { + StringBuilder s = new StringBuilder(64); + byte[] bytes = Guid.NewGuid().ToByteArray(); + for (int idx = 0; idx < 6; idx++) + s.Append((char)('A' + bytes[idx] % 26)); + s.Append('+'); + if (name.StartsWith("/")) + s.Append(name.Substring(1)); + else + s.Append(name); + return s.ToString(); + } + + /// + /// Predefined keys common to all font dictionaries. + /// + public class Keys : KeysBase + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Font for a font dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Font")] + public const string Type = "/Type"; + + /// + /// (Required) The type of font. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Subtype = "/Subtype"; + + /// + /// (Required) The PostScript name of the font. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string BaseFont = "/BaseFont"; + + /// + /// (Required except for the standard 14 fonts; must be an indirect reference) + /// A font descriptor describing the fonts metrics other than its glyph widths. + /// Note: For the standard 14 fonts, the entries FirstChar, LastChar, Widths, and + /// FontDescriptor must either all be present or all be absent. Ordinarily, they are + /// absent; specifying them enables a standard font to be overridden. + /// + [KeyInfo(KeyType.Dictionary | KeyType.MustBeIndirect, typeof(PdfFontDescriptor))] + public const string FontDescriptor = "/FontDescriptor"; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontDescriptor.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontDescriptor.cs new file mode 100644 index 00000000..9bbafe3a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontDescriptor.cs @@ -0,0 +1,351 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Fonts.OpenType; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// The PDF font descriptor flags. + /// + [Flags] + enum PdfFontDescriptorFlags + { + /// + /// All glyphs have the same width (as opposed to proportional or variable-pitch + /// fonts, which have different widths). + /// + FixedPitch = 1 << 0, + + /// + /// Glyphs have serifs, which are short strokes drawn at an angle on the top and + /// bottom of glyph stems. (Sans serif fonts do not have serifs.) + /// + Serif = 1 << 1, + + /// + /// Font contains glyphs outside the Adobe standard Latin character set. This + /// flag and the Nonsymbolic flag cannot both be set or both be clear. + /// + Symbolic = 1 << 2, + + /// + /// Glyphs resemble cursive handwriting. + /// + Script = 1 << 3, + + /// + /// Font uses the Adobe standard Latin character set or a subset of it. + /// + Nonsymbolic = 1 << 5, + + /// + /// Glyphs have dominant vertical strokes that are slanted. + /// + Italic = 1 << 6, + + /// + /// Font contains no lowercase letters; typically used for display purposes, + /// such as for titles or headlines. + /// + AllCap = 1 << 16, + + /// + /// Font contains both uppercase and lowercase letters. The uppercase letters are + /// similar to those in the regular version of the same typeface family. The glyphs + /// for the lowercase letters have the same shapes as the corresponding uppercase + /// letters, but they are sized and their proportions adjusted so that they have the + /// same size and stroke weight as lowercase glyphs in the same typeface family. + /// + SmallCap = 1 << 17, + + /// + /// Determines whether bold glyphs are painted with extra pixels even at very small + /// text sizes. + /// + ForceBold = 1 << 18, + } + + /// + /// A PDF font descriptor specifies metrics and other attributes of a simple font, + /// as distinct from the metrics of individual glyphs. + /// + public sealed class PdfFontDescriptor : PdfDictionary + { + internal PdfFontDescriptor(PdfDocument document, OpenTypeDescriptor descriptor) + : base(document) + { + _descriptor = descriptor; + Elements.SetName(Keys.Type, "/FontDescriptor"); + + Elements.SetInteger(Keys.Ascent, _descriptor.DesignUnitsToPdf(_descriptor.Ascender)); + Elements.SetInteger(Keys.CapHeight, _descriptor.DesignUnitsToPdf(_descriptor.CapHeight)); + Elements.SetInteger(Keys.Descent, _descriptor.DesignUnitsToPdf(_descriptor.Descender)); + Elements.SetInteger(Keys.Flags, (int)FlagsFromDescriptor(_descriptor)); + Elements.SetRectangle(Keys.FontBBox, new PdfRectangle( + _descriptor.DesignUnitsToPdf(_descriptor.XMin), + _descriptor.DesignUnitsToPdf(_descriptor.YMin), + _descriptor.DesignUnitsToPdf(_descriptor.XMax), + _descriptor.DesignUnitsToPdf(_descriptor.YMax))); + // not here, done in PdfFont later... + //Elements.SetName(Keys.FontName, "abc"); //descriptor.FontName); + Elements.SetReal(Keys.ItalicAngle, _descriptor.ItalicAngle); + Elements.SetInteger(Keys.StemV, _descriptor.StemV); + Elements.SetInteger(Keys.XHeight, _descriptor.DesignUnitsToPdf(_descriptor.XHeight)); + } + + //HACK OpenTypeDescriptor descriptor + internal OpenTypeDescriptor _descriptor; + + /// + /// Gets or sets the name of the font. + /// + public string FontName + { + get { return Elements.GetName(Keys.FontName); } + set { Elements.SetName(Keys.FontName, value); } + } + + /// + /// Gets a value indicating whether this instance is symbol font. + /// + public bool IsSymbolFont + { + get { return _isSymbolFont; } + } + bool _isSymbolFont; + + // HACK FlagsFromDescriptor(OpenTypeDescriptor descriptor) + PdfFontDescriptorFlags FlagsFromDescriptor(OpenTypeDescriptor descriptor) + { + PdfFontDescriptorFlags flags = 0; + _isSymbolFont = descriptor.FontFace.cmap.symbol; + flags |= descriptor.FontFace.cmap.symbol ? PdfFontDescriptorFlags.Symbolic : PdfFontDescriptorFlags.Nonsymbolic; + return flags; + } + + /// + /// Predefined keys of this dictionary. + /// + public sealed class Keys : KeysBase + { + /// + /// (Required) The type of PDF object that this dictionary describes; must be + /// FontDescriptor for a font descriptor. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "FontDescriptor")] + public const string Type = "/Type"; + + /// + /// (Required) The PostScript name of the font. This name should be the same as the + /// value of BaseFont in the font or CIDFont dictionary that refers to this font descriptor. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string FontName = "/FontName"; + + /// + /// (Optional; PDF 1.5; strongly recommended for Type 3 fonts in Tagged PDF documents) + /// A string specifying the preferred font family name. For example, for the font + /// Times Bold Italic, the FontFamily is Times. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string FontFamily = "/FontFamily"; + + /// + /// (Optional; PDF 1.5; strongly recommended for Type 3 fonts in Tagged PDF documents) + /// The font stretch value. It must be one of the following names (ordered from + /// narrowest to widest): UltraCondensed, ExtraCondensed, Condensed, SemiCondensed, + /// Normal, SemiExpanded, Expanded, ExtraExpanded or UltraExpanded. + /// Note: The specific interpretation of these values varies from font to font. + /// For example, Condensed in one font may appear most similar to Normal in another. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string FontStretch = "/FontStretch"; + + /// + /// (Optional; PDF 1.5; strongly recommended for Type 3 fonts in Tagged PDF documents) + /// The weight (thickness) component of the fully-qualified font name or font specifier. + /// The possible values are 100, 200, 300, 400, 500, 600, 700, 800, or 900, where each + /// number indicates a weight that is at least as dark as its predecessor. A value of + /// 400 indicates a normal weight; 700 indicates bold. + /// Note: The specific interpretation of these values varies from font to font. + /// For example, 300 in one font may appear most similar to 500 in another. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string FontWeight = "/FontWeight"; + + /// + /// (Required) A collection of flags defining various characteristics of the font. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Flags = "/Flags"; + + /// + /// (Required, except for Type 3 fonts) A rectangle (see Section 3.8.4, Rectangles), + /// expressed in the glyph coordinate system, specifying the font bounding box. This + /// is the smallest rectangle enclosing the shape that would result if all of the + /// glyphs of the font were placed with their origins coincident and then filled. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Required)] + public const string FontBBox = "/FontBBox"; + + /// + /// (Required) The angle, expressed in degrees counterclockwise from the vertical, of + /// the dominant vertical strokes of the font. (For example, the 9-oclock position is 90 + /// degrees, and the 3-oclock position is 90 degrees.) The value is negative for fonts + /// that slope to the right, as almost all italic fonts do. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string ItalicAngle = "/ItalicAngle"; + + /// + /// (Required, except for Type 3 fonts) The maximum height above the baseline reached + /// by glyphs in this font, excluding the height of glyphs for accented characters. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string Ascent = "/Ascent"; + + /// + /// (Required, except for Type 3 fonts) The maximum depth below the baseline reached + /// by glyphs in this font. The value is a negative number. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string Descent = "/Descent"; + + /// + /// (Optional) The spacing between baselines of consecutive lines of text. + /// Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string Leading = "/Leading"; + + /// + /// (Required for fonts that have Latin characters, except for Type 3 fonts) The vertical + /// coordinate of the top of flat capital letters, measured from the baseline. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string CapHeight = "/CapHeight"; + + /// + /// (Optional) The fonts x height: the vertical coordinate of the top of flat nonascending + /// lowercase letters (like the letter x), measured from the baseline, in fonts that have + /// Latin characters. Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string XHeight = "/XHeight"; + + /// + /// (Required, except for Type 3 fonts) The thickness, measured horizontally, of the dominant + /// vertical stems of glyphs in the font. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string StemV = "/StemV"; + + /// + /// (Optional) The thickness, measured vertically, of the dominant horizontal stems + /// of glyphs in the font. Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string StemH = "/StemH"; + + /// + /// (Optional) The average width of glyphs in the font. Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string AvgWidth = "/AvgWidth"; + + /// + /// (Optional) The maximum width of glyphs in the font. Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string MaxWidth = "/MaxWidth"; + + /// + /// (Optional) The width to use for character codes whose widths are not specified in a + /// font dictionarys Widths array. This has a predictable effect only if all such codes + /// map to glyphs whose actual widths are the same as the value of the MissingWidth entry. + /// Default value: 0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string MissingWidth = "/MissingWidth"; + + /// + /// (Optional) A stream containing a Type 1 font program. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string FontFile = "/FontFile"; + + /// + /// (Optional; PDF 1.1) A stream containing a TrueType font program. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string FontFile2 = "/FontFile2"; + + /// + /// (Optional; PDF 1.2) A stream containing a font program whose format is specified + /// by the Subtype entry in the stream dictionary. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string FontFile3 = "/FontFile3"; + + /// + /// (Optional; meaningful only in Type 1 fonts; PDF 1.1) A string listing the character + /// names defined in a font subset. The names in this string must be in PDF syntaxthat is, + /// each name preceded by a slash (/). The names can appear in any order. The name .notdef + /// should be omitted; it is assumed to exist in the font subset. If this entry is absent, + /// the only indication of a font subset is the subset tag in the FontName entry. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string CharSet = "/CharSet"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get + { + if (_meta == null) + _meta = CreateMeta(typeof(Keys)); + return _meta; + } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontTable.cs new file mode 100644 index 00000000..c4b6ed06 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFontTable.cs @@ -0,0 +1,143 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Collections.Generic; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + internal enum FontType + { + /// + /// TrueType with WinAnsi encoding. + /// + TrueType = 1, + + /// + /// TrueType with Identity-H or Identity-V encoding (unicode). + /// + Type0 = 2, + } + + /// + /// Contains all used fonts of a document. + /// + internal sealed class PdfFontTable : PdfResourceTable + { + /// + /// Initializes a new instance of this class, which is a singleton for each document. + /// + public PdfFontTable(PdfDocument document) + : base(document) + { } + + /// + /// Gets a PdfFont from an XFont. If no PdfFont already exists, a new one is created. + /// + public PdfFont GetFont(XFont font) + { + string selector = font.Selector; + if (selector == null) + { + selector = ComputeKey(font); //new FontSelector(font); + font.Selector = selector; + } + PdfFont pdfFont; + if (!_fonts.TryGetValue(selector, out pdfFont)) + { + if (font.Unicode) + pdfFont = new PdfType0Font(Owner, font, font.IsVertical); + else + pdfFont = new PdfTrueTypeFont(Owner, font); + //pdfFont.Document = _document; + Debug.Assert(pdfFont.Owner == Owner); + _fonts[selector] = pdfFont; + } + return pdfFont; + } + +#if true + /// + /// Gets a PdfFont from a font program. If no PdfFont already exists, a new one is created. + /// + public PdfFont GetFont(string idName, byte[] fontData) + { + Debug.Assert(false); + //FontSelector selector = new FontSelector(idName); + string selector = null; // ComputeKey(font); //new FontSelector(font); + PdfFont pdfFont; + if (!_fonts.TryGetValue(selector, out pdfFont)) + { + //if (font.Unicode) + pdfFont = new PdfType0Font(Owner, idName, fontData, false); + //else + // pdfFont = new PdfTrueTypeFont(_owner, font); + //pdfFont.Document = _document; + Debug.Assert(pdfFont.Owner == Owner); + _fonts[selector] = pdfFont; + } + return pdfFont; + } +#endif + + /// + /// Tries to gets a PdfFont from the font dictionary. + /// Returns null if no such PdfFont exists. + /// + public PdfFont TryGetFont(string idName) + { + Debug.Assert(false); + //FontSelector selector = new FontSelector(idName); + string selector = null; + PdfFont pdfFont; + _fonts.TryGetValue(selector, out pdfFont); + return pdfFont; + } + + internal static string ComputeKey(XFont font) + { + XGlyphTypeface glyphTypeface = font.GlyphTypeface; + string key = glyphTypeface.Fontface.FullFaceName.ToLowerInvariant() + + (glyphTypeface.IsBold ? "/b" : "") + (glyphTypeface.IsItalic ? "/i" : "") + font.Unicode; + return key; + } + + /// + /// Map from PdfFontSelector to PdfFont. + /// + readonly Dictionary _fonts = new Dictionary(); + + public void PrepareForSave() + { + foreach (PdfFont font in _fonts.Values) + font.PrepareForSave(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObject.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObject.cs new file mode 100644 index 00000000..e0eea71b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObject.cs @@ -0,0 +1,509 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an external form object (e.g. an imported page). + /// + public sealed class PdfFormXObject : PdfXObject, IContentStream + { + internal PdfFormXObject(PdfDocument thisDocument) + : base(thisDocument) + { + Elements.SetName(Keys.Type, "/XObject"); + Elements.SetName(Keys.Subtype, "/Form"); + } + + internal PdfFormXObject(PdfDocument thisDocument, XForm form) + : base(thisDocument) + { + // BUG: form is not used + Elements.SetName(Keys.Type, "/XObject"); + Elements.SetName(Keys.Subtype, "/Form"); + + //if (form.IsTemplate) + //{ } + } + + internal double DpiX + { + get { return _dpiX; } + set { _dpiX = value; } + } + double _dpiX = 72; + + internal double DpiY + { + get { return _dpiY; } + set { _dpiY = value; } + } + double _dpiY = 72; + + internal PdfFormXObject(PdfDocument thisDocument, PdfImportedObjectTable importedObjectTable, XPdfForm form) + : base(thisDocument) + { + Debug.Assert(importedObjectTable != null); + Debug.Assert(ReferenceEquals(thisDocument, importedObjectTable.Owner)); + Elements.SetName(Keys.Type, "/XObject"); + Elements.SetName(Keys.Subtype, "/Form"); + + if (form.IsTemplate) + { + Debug.Assert(importedObjectTable == null); + // TODO more initialization here??? + return; + } + + XPdfForm pdfForm = form; + // Get import page + PdfPages importPages = importedObjectTable.ExternalDocument.Pages; + if (pdfForm.PageNumber < 1 || pdfForm.PageNumber > importPages.Count) + PSSR.ImportPageNumberOutOfRange(pdfForm.PageNumber, importPages.Count, form._path); + PdfPage importPage = importPages[pdfForm.PageNumber - 1]; + + // Import resources + PdfItem res = importPage.Elements["/Resources"]; + if (res != null) // unlikely but possible + { +#if true + // Get root object + PdfObject root; + if (res is PdfReference) + root = ((PdfReference)res).Value; + else + root = (PdfDictionary)res; + + root = ImportClosure(importedObjectTable, thisDocument, root); + // If the root was a direct object, make it indirect. + if (root.Reference == null) + thisDocument._irefTable.Add(root); + + Debug.Assert(root.Reference != null); + Elements["/Resources"] = root.Reference; +#else + // Get transitive closure + PdfObject[] resources = importPage.Owner.Internals.GetClosure(resourcesRoot); + int count = resources.Length; +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + Debug.Assert(resources[idx].XRef != null); + Debug.Assert(resources[idx].XRef.Document != null); + Debug.Assert(resources[idx].Document != null); + if (resources[idx].ObjectID.ObjectNumber == 12) + GetType(); + } +#endif + // 1st step. Already imported objects are reused and new ones are cloned. + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = resources[idx]; + if (importedObjectTable.Contains(obj.ObjectID)) + { + // external object was already imported + PdfReference iref = importedObjectTable[obj.ObjectID]; + Debug.Assert(iref != null); + Debug.Assert(iref.Value != null); + Debug.Assert(iref.Document == Owner); + // replace external object by the already clone counterpart + resources[idx] = iref.Value; + } + else + { + // External object was not imported earlier and must be cloned + PdfObject clone = obj.Clone(); + Debug.Assert(clone.Reference == null); + clone.Document = Owner; + if (obj.Reference != null) + { + // add it to this (the importer) document + Owner.irefTable.Add(clone); + Debug.Assert(clone.Reference != null); + // save old object identifier + importedObjectTable.Add(obj.ObjectID, clone.Reference); + //Debug.WriteLine("Cloned: " + obj.ObjectID.ToString()); + } + else + { + // The root object (the /Resources value) is not an indirect object + Debug.Assert(idx == 0); + // add it to this (the importer) document + Owner.irefTable.Add(clone); + Debug.Assert(clone.Reference != null); + } + // replace external object by its clone + resources[idx] = clone; + } + } +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + Debug.Assert(resources[idx].XRef != null); + Debug.Assert(resources[idx].XRef.Document != null); + Debug.Assert(resources[idx].Document != null); + if (resources[idx].ObjectID.ObjectNumber == 12) + GetType(); + } +#endif + + // 2nd step. Fix up indirect references that still refers to the import document. + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = resources[idx]; + Debug.Assert(obj.Owner != null); + FixUpObject(importedObjectTable, importedObjectTable.Owner, obj); + } + + // Set resources key to the root of the clones + Elements["/Resources"] = resources[0].Reference; +#endif + } + + // Take /Rotate into account. + PdfRectangle rect = importPage.Elements.GetRectangle(PdfPage.Keys.MediaBox); + // Reduce rotation to 0, 90, 180, or 270. + int rotate = (importPage.Elements.GetInteger(PdfPage.Keys.Rotate) % 360 + 360) % 360; + //rotate = 0; + if (rotate == 0) + { + // Set bounding box to media box. + Elements["/BBox"] = rect; + } + else + { + // TODO: Have to adjust bounding box? (I think not, but I'm not sure -> wait for problem) + Elements["/BBox"] = rect; + + // Rotate the image such that it is upright. + XMatrix matrix = new XMatrix(); + double width = rect.Width; + double height = rect.Height; + matrix.RotateAtPrepend(-rotate, new XPoint(width / 2, height / 2)); + + // Translate the image such that its center lies on the center of the rotated bounding box. + double offset = (height - width) / 2; + if (rotate == 90) + { + // TODO It seems we can simplify this as the sign of offset changes too. + if (height > width) + matrix.TranslatePrepend(offset, offset); // Tested. + else + matrix.TranslatePrepend(offset, offset); // TODO Test case. + } + else if (rotate == 270) + { + // TODO It seems we can simplify this as the sign of offset changes too. + if (height > width) + matrix.TranslatePrepend(-offset, -offset); // Tested. + else + matrix.TranslatePrepend(-offset, -offset); // Tested. + } + + //string item = "[" + PdfEncoders.ToString(matrix) + "]"; + //Elements[Keys.Matrix] = new PdfLiteral(item); + Elements.SetMatrix(Keys.Matrix, matrix); + } + + // Preserve filter because the content keeps unmodified. + PdfContent content = importPage.Contents.CreateSingleContent(); +#if !DEBUG + content.Compressed = true; +#endif + PdfItem filter = content.Elements["/Filter"]; + if (filter != null) + Elements["/Filter"] = filter.Clone(); + + // (no cloning needed because the bytes keep untouched) + Stream = content.Stream; // new PdfStream(bytes, this); + Elements.SetInteger("/Length", content.Stream.Value.Length); + } + + /// + /// Gets the PdfResources object of this form. + /// + public PdfResources Resources + { + get + { + if (_resources == null) + _resources = (PdfResources)Elements.GetValue(Keys.Resources, VCF.Create); + return _resources; + } + } + PdfResources _resources; + + PdfResources IContentStream.Resources + { + get { return Resources; } + } + + internal string GetFontName(XFont font, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(font); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(XFont font, out PdfFont pdfFont) + { + return GetFontName(font, out pdfFont); + } + + /// + /// Gets the resource name of the specified font data within this form XObject. + /// + internal string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(idName, fontData); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + return GetFontName(idName, fontData, out pdfFont); + } + + string IContentStream.GetImageName(XImage image) + { + throw new NotImplementedException(); + } + + string IContentStream.GetFormName(XForm form) + { + throw new NotImplementedException(); + } + +#if keep_code_some_time_as_reference + /// + /// Replace all indirect references to external objects by their cloned counterparts + /// owned by the importer document. + /// + void FixUpObject_old(PdfImportedObjectTable iot, PdfObject value) + { + // TODO: merge with PdfXObject.FixUpObject + PdfDictionary dict; + PdfArray array; + if ((dict = value as PdfDictionary) != null) + { + // Set document for cloned direct objects + if (dict.Owner == null) + dict.Document = Owner; + else + Debug.Assert(dict.Owner == Owner); + + // Search for indirect references in all keys + PdfName[] names = dict.Elements.KeyNames; + foreach (PdfName name in names) + { + PdfItem item = dict.Elements[name]; + // Is item an iref? + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Does the iref already belong to this document? + if (iref.Document == Owner) + { + // Yes: fine + continue; + } + else + { + Debug.Assert(iref.Document == iot.ExternalDocument); + // No: replace with iref of cloned object + PdfReference newXRef = iot[iref.ObjectID]; + Debug.Assert(newXRef != null); + Debug.Assert(newXRef.Document == Owner); + dict.Elements[name] = newXRef; + } + } + else if (item is PdfObject) + { + // Fix up inner objects + FixUpObject_old(iot, (PdfObject)item); + } + } + } + else if ((array = value as PdfArray) != null) + { + // Set document for cloned direct objects + if (array.Owner == null) + array.Document = Owner; + else + Debug.Assert(array.Owner == Owner); + + // Search for indirect references in all array elements + int count = array.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = array.Elements[idx]; + // Is item an iref? + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Does the iref belongs to this document? + if (iref.Document == Owner) + { + // Yes: fine + continue; + } + else + { + Debug.Assert(iref.Document == iot.ExternalDocument); + // No: replace with iref of cloned object + PdfReference newXRef = iot[iref.ObjectID]; + Debug.Assert(newXRef != null); + Debug.Assert(newXRef.Document == Owner); + array.Elements[idx] = newXRef; + } + } + else if (item is PdfObject) + { + // Fix up inner objects + FixUpObject_old(iot, (PdfObject)item); + } + } + } + } +#endif + + // /// + // /// Returns ??? + // /// + // public override string ToString() + // { + // return "Form"; + // } + + /// + /// Predefined keys of this dictionary. + /// + public sealed new class Keys : PdfXObject.Keys + { + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be XObject for a form XObject. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Required) The type of XObject that this dictionary describes; must be Form + /// for a form XObject. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Subtype = "/Subtype"; + + /// + /// (Optional) A code identifying the type of form XObject that this dictionary + /// describes. The only valid value defined at the time of publication is 1. + /// Default value: 1. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string FormType = "/FormType"; + + /// + /// (Required) An array of four numbers in the form coordinate system, giving the + /// coordinates of the left, bottom, right, and top edges, respectively, of the + /// form XObjects bounding box. These boundaries are used to clip the form XObject + /// and to determine its size for caching. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Required)] + public const string BBox = "/BBox"; + + /// + /// (Optional) An array of six numbers specifying the form matrix, which maps + /// form space into user space. + /// Default value: the identity matrix [1 0 0 1 0 0]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Matrix = "/Matrix"; + + /// + /// (Optional but strongly recommended; PDF 1.2) A dictionary specifying any + /// resources (such as fonts and images) required by the form XObject. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResources))] + public const string Resources = "/Resources"; + + /// + /// (Optional; PDF 1.4) A group attributes dictionary indicating that the contents + /// of the form XObject are to be treated as a group and specifying the attributes + /// of that group (see Section 4.9.2, Group XObjects). + /// Note: If a Ref entry (see below) is present, the group attributes also apply to the + /// external page imported by that entry, which allows such an imported page to be + /// treated as a group without further modification. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Group = "/Group"; + + // further keys: + //Ref + //Metadata + //PieceInfo + //LastModified + //StructParent + //StructParents + //OPI + //OC + //Name + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObjectTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObjectTable.cs new file mode 100644 index 00000000..ec71660f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfFormXObjectTable.cs @@ -0,0 +1,228 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Globalization; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Contains all external PDF files from which PdfFormXObjects are imported into the current document. + /// + internal sealed class PdfFormXObjectTable : PdfResourceTable + { + // The name PdfFormXObjectTable is technically not correct, because in contrast to PdfFontTable + // or PdfImageTable this class holds no PdfFormXObject objects. Actually it holds instances of + // the class ImportedObjectTable, one for each external document. The PdfFormXObject instances + // are not cached, because they hold a transformation matrix that make them unique. If the user + // wants to use a particual page of a PdfFormXObject more than once, he must reuse the object + // before he changes the PageNumber or the transformation matrix. In other words this class + // caches the indirect objects of an external form, not the form itself. + + /// + /// Initializes a new instance of this class, which is a singleton for each document. + /// + public PdfFormXObjectTable(PdfDocument document) + : base(document) + { } + + /// + /// Gets a PdfFormXObject from an XPdfForm. Because the returned objects must be unique, always + /// a new instance of PdfFormXObject is created if none exists for the specified form. + /// + public PdfFormXObject GetForm(XForm form) + { + // If the form already has a PdfFormXObject, return it. + if (form._pdfForm != null) + { + Debug.Assert(form.IsTemplate, "An XPdfForm must not have a PdfFormXObject."); + if (ReferenceEquals(form._pdfForm.Owner, Owner)) + return form._pdfForm; + //throw new InvalidOperationException("Because of a current limitation of PDFsharp an XPdfForm object can be used only within one single PdfDocument."); + + // Dispose PdfFromXObject when document has changed + form._pdfForm = null; + } + + XPdfForm pdfForm = form as XPdfForm; + if (pdfForm != null) + { + // Is the external PDF file from which is imported already known for the current document? + Selector selector = new Selector(form); + PdfImportedObjectTable importedObjectTable; + if (!_forms.TryGetValue(selector, out importedObjectTable)) + { + // No: Get the external document from the form and create ImportedObjectTable. + PdfDocument doc = pdfForm.ExternalDocument; + importedObjectTable = new PdfImportedObjectTable(Owner, doc); + _forms[selector] = importedObjectTable; + } + + PdfFormXObject xObject = importedObjectTable.GetXObject(pdfForm.PageNumber); + if (xObject == null) + { + xObject = new PdfFormXObject(Owner, importedObjectTable, pdfForm); + importedObjectTable.SetXObject(pdfForm.PageNumber, xObject); + } + return xObject; + } + Debug.Assert(form.GetType() == typeof(XForm)); + form._pdfForm = new PdfFormXObject(Owner, form); + return form._pdfForm; + } + + /// + /// Gets the imported object table. + /// + public PdfImportedObjectTable GetImportedObjectTable(PdfPage page) + { + // Is the external PDF file from which is imported already known for the current document? + Selector selector = new Selector(page); + PdfImportedObjectTable importedObjectTable; + if (!_forms.TryGetValue(selector, out importedObjectTable)) + { + importedObjectTable = new PdfImportedObjectTable(Owner, page.Owner); + _forms[selector] = importedObjectTable; + } + return importedObjectTable; + } + + /// + /// Gets the imported object table. + /// + public PdfImportedObjectTable GetImportedObjectTable(PdfDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + + // Is the external PDF file from which is imported already known for the current document? + Selector selector = new Selector(document); + PdfImportedObjectTable importedObjectTable; + if (!_forms.TryGetValue(selector, out importedObjectTable)) + { + // Create new table for document. + importedObjectTable = new PdfImportedObjectTable(Owner, document); + _forms[selector] = importedObjectTable; + } + return importedObjectTable; + } + + public void DetachDocument(PdfDocument.DocumentHandle handle) + { + if (handle.IsAlive) + { + foreach (Selector selector in _forms.Keys) + { + PdfImportedObjectTable table = _forms[selector]; + if (table.ExternalDocument != null && table.ExternalDocument.Handle == handle) + { + _forms.Remove(selector); + break; + } + } + } + + // Clean table + bool itemRemoved = true; + while (itemRemoved) + { + itemRemoved = false; + foreach (Selector selector in _forms.Keys) + { + PdfImportedObjectTable table = _forms[selector]; + if (table.ExternalDocument == null) + { + _forms.Remove(selector); + itemRemoved = true; + break; + } + } + } + } + + /// + /// Map from Selector to PdfImportedObjectTable. + /// + readonly Dictionary _forms = new Dictionary(); + + /// + /// A collection of information that uniquely identifies a particular ImportedObjectTable. + /// + public class Selector + { + /// + /// Initializes a new instance of FormSelector from an XPdfForm. + /// + public Selector(XForm form) + { + // HACK: just use full path to identify + _path = form._path.ToLowerInvariant(); + } + + /// + /// Initializes a new instance of FormSelector from a PdfPage. + /// + public Selector(PdfPage page) + { + PdfDocument owner = page.Owner; + _path = "*" + owner.Guid.ToString("B"); + _path = _path.ToLowerInvariant(); + } + + public Selector(PdfDocument document) + { + _path = "*" + document.Guid.ToString("B"); + _path = _path.ToLowerInvariant(); + } + + public string Path + { + get { return _path; } + set { _path = value; } + } + string _path; + + public override bool Equals(object obj) + { + Selector selector = obj as Selector; + if (selector == null) + return false; + return _path == selector._path; + } + + public override int GetHashCode() + { + return _path.GetHashCode(); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfGroupAttributes.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfGroupAttributes.cs new file mode 100644 index 00000000..d9e525f1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfGroupAttributes.cs @@ -0,0 +1,90 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF group XObject. + /// + public abstract class PdfGroupAttributes : PdfDictionary + { + internal PdfGroupAttributes(PdfDocument thisDocument) + : base(thisDocument) + { + Elements.SetName(Keys.Type, "/Group"); + } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : KeysBase + { + /// + ///(Optional) The type of PDF object that this dictionary describes; + ///if present, must be Group for a group attributes dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Required) The group subtype, which identifies the type of group whose + /// attributes this dictionary describes and determines the format and meaning + /// of the dictionarys remaining entries. The only group subtype defined in + /// PDF 1.4 is Transparency. Other group subtypes may be added in the future. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string S = "/S"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.FaxEncode.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.FaxEncode.cs new file mode 100644 index 00000000..efc41dcf --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.FaxEncode.cs @@ -0,0 +1,840 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// Thomas Hövel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// Some routines were translated from LibTiff. +// LibTiff copyright notice: +// Copyright (c) 1988-1997 Sam Leffler +// Copyright (c) 1991-1997 Silicon Graphics, Inc. +// +// Permission to use, copy, modify, distribute, and sell this software and +// its documentation for any purpose is hereby granted without fee, provided +// that (i) the above copyright notices and this permission notice appear in +// all copies of the software and related documentation, and (ii) the names of +// Sam Leffler and Silicon Graphics may not be used in any advertising or +// publicity relating to the software without the specific, prior written +// permission of Sam Leffler and Silicon Graphics. +// +// THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, +// EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY +// WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. +// +// IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR +// ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, +// OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +// WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF +// LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +// OF THIS SOFTWARE. + +#endregion + +#define USE_GOTO +using System; +using System.Diagnostics; + +namespace PdfSharp.Pdf.Advanced +{ + partial class PdfImage + { + internal readonly static uint[] WhiteTerminatingCodes = + { + 0x35, 8, //00110101 // 0 + 0x07, 6, //000111 + 0x07, 4, //0111 + 0x08, 4, //1000 + 0x0b, 4, //1011 + 0x0c, 4, //1100 + 0x0e, 4, //1110 + 0x0f, 4, //1111 + 0x13, 5, //10011 + 0x14, 5, //10100 + 0x07, 5, //00111 // 10 + 0x08, 5, //01000 + 0x08, 6, //001000 + 0x03, 6, //000011 + 0x34, 6, //110100 + 0x35, 6, //110101 + 0x2a, 6, //101010 // 16 + 0x2b, 6, //101011 + 0x27, 7, //0100111 + 0x0c, 7, //0001100 + 0x08, 7, //0001000 // 20 + 0x17, 7, //0010111 + 0x03, 7, //0000011 + 0x04, 7, //0000100 + 0x28, 7, //0101000 + 0x2b, 7, //0101011 + 0x13, 7, //0010011 + 0x24, 7, //0100100 + 0x18, 7, //0011000 + 0x02, 8, //00000010 + 0x03, 8, //00000011 // 30 + 0x1a, 8, //00011010 + 0x1b, 8, //00011011 // 32 + 0x12, 8, //00010010 + 0x13, 8, //00010011 + 0x14, 8, //00010100 + 0x15, 8, //00010101 + 0x16, 8, //00010110 + 0x17, 8, //00010111 + 0x28, 8, //00101000 + 0x29, 8, //00101001 // 40 + 0x2a, 8, //00101010 + 0x2b, 8, //00101011 + 0x2c, 8, //00101100 + 0x2d, 8, //00101101 + 0x04, 8, //00000100 + 0x05, 8, //00000101 + 0x0a, 8, //00001010 + 0x0b, 8, //00001011 // 48 + 0x52, 8, //01010010 + 0x53, 8, //01010011 // 50 + 0x54, 8, //01010100 + 0x55, 8, //01010101 + 0x24, 8, //00100100 + 0x25, 8, //00100101 + 0x58, 8, //01011000 + 0x59, 8, //01011001 + 0x5a, 8, //01011010 + 0x5b, 8, //01011011 + 0x4a, 8, //01001010 + 0x4b, 8, //01001011 // 60 + 0x32, 8, //00110010 + 0x33, 8, //00110011 + 0x34, 8, //00110100 // 63 + }; + + internal readonly static uint[] BlackTerminatingCodes = + { + 0x37, 10, //0000110111 // 0 + 0x02, 3, //010 + 0x03, 2, //11 + 0x02, 2, //10 + 0x03, 3, //011 + 0x03, 4, //0011 + 0x02, 4, //0010 + 0x03, 5, //00011 + 0x05, 6, //000101 + 0x04, 6, //000100 + 0x04, 7, //0000100 + 0x05, 7, //0000101 + 0x07, 7, //0000111 + 0x04, 8, //00000100 + 0x07, 8, //00000111 + 0x18, 9, //000011000 + 0x17, 10, //0000010111 // 16 + 0x18, 10, //0000011000 + 0x08, 10, //0000001000 + 0x67, 11, //00001100111 + 0x68, 11, //00001101000 + 0x6c, 11, //00001101100 + 0x37, 11, //00000110111 + 0x28, 11, //00000101000 + 0x17, 11, //00000010111 + 0x18, 11, //00000011000 + 0xca, 12, //000011001010 + 0xcb, 12, //000011001011 + 0xcc, 12, //000011001100 + 0xcd, 12, //000011001101 + 0x68, 12, //000001101000 // 30 + 0x69, 12, //000001101001 + 0x6a, 12, //000001101010 // 32 + 0x6b, 12, //000001101011 + 0xd2, 12, //000011010010 + 0xd3, 12, //000011010011 + 0xd4, 12, //000011010100 + 0xd5, 12, //000011010101 + 0xd6, 12, //000011010110 + 0xd7, 12, //000011010111 + 0x6c, 12, //000001101100 + 0x6d, 12, //000001101101 + 0xda, 12, //000011011010 + 0xdb, 12, //000011011011 + 0x54, 12, //000001010100 + 0x55, 12, //000001010101 + 0x56, 12, //000001010110 + 0x57, 12, //000001010111 + 0x64, 12, //000001100100 // 48 + 0x65, 12, //000001100101 + 0x52, 12, //000001010010 + 0x53, 12, //000001010011 + 0x24, 12, //000000100100 + 0x37, 12, //000000110111 + 0x38, 12, //000000111000 + 0x27, 12, //000000100111 + 0x28, 12, //000000101000 + 0x58, 12, //000001011000 + 0x59, 12, //000001011001 + 0x2b, 12, //000000101011 + 0x2c, 12, //000000101100 + 0x5a, 12, //000001011010 + 0x66, 12, //000001100110 + 0x67, 12, //000001100111 // 63 + }; + + internal readonly static uint[] WhiteMakeUpCodes = + { + 0x1b, 5, //11011 64 // 0 + 0x12, 5, //10010 128 + 0x17, 6, //010111 192 + 0x37, 7, //0110111 256 + 0x36, 8, //00110110 320 + 0x37, 8, //00110111 384 + 0x64, 8, //01100100 448 + 0x65, 8, //01100101 512 + 0x68, 8, //01101000 576 + 0x67, 8, //01100111 640 + 0xcc, 9, //011001100 704 // 10 + 0xcd, 9, //011001101 768 + 0xd2, 9, //011010010 832 + 0xd3, 9, //011010011 896 + 0xd4, 9, //011010100 960 + 0xd5, 9, //011010101 1024 + 0xd6, 9, //011010110 1088 // 16 + 0xd7, 9, //011010111 1152 + 0xd8, 9, //011011000 1216 + 0xd9, 9, //011011001 1280 + 0xda, 9, //011011010 1344 + 0xdb, 9, //011011011 1408 + 0x98, 9, //010011000 1472 + 0x99, 9, //010011001 1536 + 0x9a, 9, //010011010 1600 + 0x18, 6, //011000 1664 + 0x9b, 9, //010011011 1728 + // Common codes for white and black: + 0x08, 11, //00000001000 1792 + 0x0c, 11, //00000001100 1856 + 0x0d, 11, //00000001101 1920 + 0x12, 12, //000000010010 1984 + 0x13, 12, //000000010011 2048 + 0x14, 12, //000000010100 2112 // 32 + 0x15, 12, //000000010101 2176 + 0x16, 12, //000000010110 2240 + 0x17, 12, //000000010111 2304 + 0x1c, 12, //000000011100 2368 + 0x1d, 12, //000000011101 2432 + 0x1e, 12, //000000011110 2496 + 0x1f, 12, //000000011111 2560 + 0x01, 12, //000000000001 EOL // 40 + }; + + internal readonly static uint[] BlackMakeUpCodes = + { + 0x0f, 10, //0000001111 64 // 0 + 0xc8, 12, //000011001000 128 + 0xc9, 12, //000011001001 192 + 0x5b, 12, //000001011011 256 + 0x33, 12, //000000110011 320 + 0x34, 12, //000000110100 384 + 0x35, 12, //000000110101 448 + 0x6c, 13, //0000001101100 512 + 0x6d, 13, //0000001101101 576 + 0x4a, 13, //0000001001010 640 + 0x4b, 13, //0000001001011 704 + 0x4c, 13, //0000001001100 768 + 0x4d, 13, //0000001001101 832 + 0x72, 13, //0000001110010 896 + 0x73, 13, //0000001110011 960 + 0x74, 13, //0000001110100 1024 + 0x75, 13, //0000001110101 1088 // 16 + 0x76, 13, //0000001110110 1152 + 0x77, 13, //0000001110111 1216 + 0x52, 13, //0000001010010 1280 + 0x53, 13, //0000001010011 1344 + 0x54, 13, //0000001010100 1408 + 0x55, 13, //0000001010101 1472 + 0x5a, 13, //0000001011010 1536 + 0x5b, 13, //0000001011011 1600 + 0x64, 13, //0000001100100 1664 + 0x65, 13, //0000001100101 1728 + // Common codes for white and black: + 0x08, 11, //00000001000 1792 + 0x0c, 11, //00000001100 1856 + 0x0d, 11, //00000001101 1920 + 0x12, 12, //000000010010 1984 + 0x13, 12, //000000010011 2048 + 0x14, 12, //000000010100 2112 // 32 + 0x15, 12, //000000010101 2176 + 0x16, 12, //000000010110 2240 + 0x17, 12, //000000010111 2304 + 0x1c, 12, //000000011100 2368 + 0x1d, 12, //000000011101 2432 + 0x1e, 12, //000000011110 2496 + 0x1f, 12, //000000011111 2560 + 0x01, 12, //000000000001 EOL // 40 + }; + + internal readonly static uint[] HorizontalCodes = { 0x1, 3 }; /* 001 */ + internal readonly static uint[] PassCodes = { 0x1, 4, }; /* 0001 */ + internal readonly static uint[] VerticalCodes = + { + 0x03, 7, /* 0000 011 */ + 0x03, 6, /* 0000 11 */ + 0x03, 3, /* 011 */ + 0x1, 1, /* 1 */ + 0x2, 3, /* 010 */ + 0x02, 6, /* 0000 10 */ + 0x02, 7, /* 0000 010 */ + }; + + readonly static uint[] _zeroRuns = + { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x00 - 0x0f */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 - 0x1f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 - 0x2f */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 - 0x3f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x50 - 0x5f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x70 - 0x7f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 - 0xff */ + }; + + readonly static uint[] _oneRuns = + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 - 0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 - 0x8f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 - 0x9f */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xa0 - 0xaf */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xb0 - 0xbf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 - 0xcf */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xd0 - 0xdf */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 - 0xef */ + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8, /* 0xf0 - 0xff */ + }; + + /// + /// Counts the consecutive one bits in an image line. + /// + /// The reader. + /// The bits left. + private static uint CountOneBits(BitReader reader, uint bitsLeft) + { + uint found = 0; + for (;;) + { + uint bits; + int @byte = reader.PeekByte(out bits); + uint hits = _oneRuns[@byte]; + if (hits < bits) + { + if (hits > 0) + reader.SkipBits(hits); + found += hits; + return found >= bitsLeft ? bitsLeft : found; + } + found += bits; + if (found >= bitsLeft) + return bitsLeft; + reader.NextByte(); + } + } + + /// + /// Counts the consecutive zero bits in an image line. + /// + /// The reader. + /// The bits left. + private static uint CountZeroBits(BitReader reader, uint bitsLeft) + { + uint found = 0; + for (;;) + { + uint bits; + int @byte = reader.PeekByte(out bits); + uint hits = _zeroRuns[@byte]; + if (hits < bits) + { + if (hits > 0) + reader.SkipBits(hits); + found += hits; + return found >= bitsLeft ? bitsLeft : found; + } + found += bits; + if (found >= bitsLeft) + return bitsLeft; + reader.NextByte(); + } + } + + /// + /// Returns the offset of the next bit in the range + /// [bitStart..bitEnd] that is different from the + /// specified color. The end, bitEnd, is returned + /// if no such bit exists. + /// + /// The reader. + /// The offset of the start bit. + /// The offset of the end bit. + /// If set to true searches "one" (i. e. white), otherwise searches black. + /// The offset of the first non-matching bit. + private static uint FindDifference(BitReader reader, uint bitStart, uint bitEnd, bool searchOne) + { + // Translated from LibTiff + reader.SetPosition(bitStart); + return (bitStart + (searchOne ? CountOneBits(reader, bitEnd - bitStart) : CountZeroBits(reader, bitEnd - bitStart))); + } + + /// + /// Returns the offset of the next bit in the range + /// [bitStart..bitEnd] that is different from the + /// specified color. The end, bitEnd, is returned + /// if no such bit exists. + /// Like FindDifference, but also check the + /// starting bit against the end in case start > end. + /// + /// The reader. + /// The offset of the start bit. + /// The offset of the end bit. + /// If set to true searches "one" (i. e. white), otherwise searches black. + /// The offset of the first non-matching bit. + private static uint FindDifferenceWithCheck(BitReader reader, uint bitStart, uint bitEnd, bool searchOne) + { + // Translated from LibTiff + return ((bitStart < bitEnd) ? FindDifference(reader, bitStart, bitEnd, searchOne) : bitEnd); + } + + /// + /// 2d-encode a row of pixels. Consult the CCITT documentation for the algorithm. + /// + /// The writer. + /// Offset of image data in bitmap file. + /// The bitmap file. + /// Index of the current row. + /// Index of the reference row (0xffffffff if there is none). + /// The width of the image. + /// The height of the image. + /// The bytes per line in the bitmap file. + static void FaxEncode2DRow(BitWriter writer, uint bytesFileOffset, byte[] imageBits, uint currentRow, uint referenceRow, uint width, uint height, uint bytesPerLineBmp) + { + // Translated from LibTiff + uint bytesOffsetRead = bytesFileOffset + (height - 1 - currentRow) * bytesPerLineBmp; + BitReader reader = new BitReader(imageBits, bytesOffsetRead, width); + BitReader readerReference; + if (referenceRow != 0xffffffff) + { + uint bytesOffsetReadReference = bytesFileOffset + (height - 1 - referenceRow) * bytesPerLineBmp; + readerReference = new BitReader(imageBits, bytesOffsetReadReference, width); + } + else + { + byte[] tmpImageBits = new byte[bytesPerLineBmp]; + for (int i = 0; i < bytesPerLineBmp; ++i) + tmpImageBits[i] = 255; + readerReference = new BitReader(tmpImageBits, 0, width); + } + + uint a0 = 0; + uint a1 = !reader.GetBit(0) ? 0 : FindDifference(reader, 0, width, true); + uint b1 = !readerReference.GetBit(0) ? 0 : FindDifference(readerReference, 0, width, true); + // ReSharper disable TooWideLocalVariableScope + uint a2, b2; + // ReSharper restore TooWideLocalVariableScope + + for (;;) + { + b2 = FindDifferenceWithCheck(readerReference, b1, width, readerReference.GetBit(b1)); + if (b2 >= a1) + { + int d = (int)b1 - (int)a1; + if (!(-3 <= d && d <= 3)) + { + /* horizontal mode */ + a2 = FindDifferenceWithCheck(reader, a1, width, reader.GetBit(a1)); + writer.WriteTableLine(HorizontalCodes, 0); + + if (a0 + a1 == 0 || reader.GetBit(a0)) + { + WriteSample(writer, a1 - a0, true); + WriteSample(writer, a2 - a1, false); + } + else + { + WriteSample(writer, a1 - a0, false); + WriteSample(writer, a2 - a1, true); + } + a0 = a2; + } + else + { + /* vertical mode */ + writer.WriteTableLine(VerticalCodes, (uint)(d + 3)); + a0 = a1; + } + } + else + { + /* pass mode */ + writer.WriteTableLine(PassCodes, 0); + a0 = b2; + } + if (a0 >= width) + break; + bool bitA0 = reader.GetBit(a0); + a1 = FindDifference(reader, a0, width, bitA0/*reader.GetBit(a0)*/); + b1 = FindDifference(readerReference, a0, width, !bitA0/*reader.GetBit(a0)*/); + b1 = FindDifferenceWithCheck(readerReference, b1, width, bitA0/*reader.GetBit(a0)*/); + } + } + + /// + /// Encodes a bitonal bitmap using 1D CCITT fax encoding. + /// + /// Space reserved for the fax encoded bitmap. An exception will be thrown if this buffer is too small. + /// The bitmap to be encoded. + /// Offset of image data in bitmap file. + /// The width of the image. + /// The height of the image. + /// The size of the fax encoded image (0 on failure). + private static int DoFaxEncoding(ref byte[] imageData, byte[] imageBits, uint bytesFileOffset, uint width, uint height) + { + try + { + uint bytesPerLineBmp = ((width + 31) / 32) * 4; + BitWriter writer = new BitWriter(ref imageData); + for (uint y = 0; y < height; ++y) + { + uint bytesOffsetRead = bytesFileOffset + (height - 1 - y) * bytesPerLineBmp; + BitReader reader = new BitReader(imageBits, bytesOffsetRead, width); + for (uint bitsRead = 0; bitsRead < width;) + { + uint white = CountOneBits(reader, width - bitsRead); + WriteSample(writer, white, true); + bitsRead += white; + if (bitsRead < width) + { + uint black = CountZeroBits(reader, width - bitsRead); + WriteSample(writer, black, false); + bitsRead += black; + } + } + } + writer.FlushBuffer(); + return writer.BytesWritten(); + } + catch (Exception /*ex*/) + { + //ex.GetType(); + return 0; + } + } + + /// + /// Encodes a bitonal bitmap using 2D group 4 CCITT fax encoding. + /// + /// Space reserved for the fax encoded bitmap. An exception will be thrown if this buffer is too small. + /// The bitmap to be encoded. + /// Offset of image data in bitmap file. + /// The width of the image. + /// The height of the image. + /// The size of the fax encoded image (0 on failure). + internal static int DoFaxEncodingGroup4(ref byte[] imageData, byte[] imageBits, uint bytesFileOffset, uint width, uint height) + { + try + { + uint bytesPerLineBmp = ((width + 31) / 32) * 4; + BitWriter writer = new BitWriter(ref imageData); + for (uint y = 0; y < height; ++y) + { + FaxEncode2DRow(writer, bytesFileOffset, imageBits, y, (y != 0) ? y - 1 : 0xffffffff, width, height, bytesPerLineBmp); + } + writer.FlushBuffer(); + return writer.BytesWritten(); + } + catch (Exception ex) + { + ex.GetType(); + return 0; + } + } + + /// + /// Writes the image data. + /// + /// The writer. + /// The count of bits (pels) to encode. + /// The color of the pels. + private static void WriteSample(BitWriter writer, uint count, bool white) + { + uint[] terminatingCodes = white ? WhiteTerminatingCodes : BlackTerminatingCodes; + uint[] makeUpCodes = white ? WhiteMakeUpCodes : BlackMakeUpCodes; + + // The make-up code for 2560 will be written as often as required: + while (count >= 2624) + { + writer.WriteTableLine(makeUpCodes, 39); // Magic: 2560 + count -= 2560; + } + // A make-up code for a multiple of 64 will be written if required: + if (count > 63) + { + uint line = count / 64 - 1; + writer.WriteTableLine(makeUpCodes, line); + count -= (line + 1) * 64; + } + // And finally the terminating code for the remaining value (0 through 63): + writer.WriteTableLine(terminatingCodes, count); + } + } + + /// + /// The BitReader class is a helper to read bits from an in-memory bitmap file. + /// + class BitReader + { + readonly byte[] _imageBits; + uint _bytesOffsetRead; + readonly uint _bytesFileOffset; + byte _buffer; + uint _bitsInBuffer; + readonly uint _bitsTotal; // Bits we may read (bits per image line) + + /// + /// Initializes a new instance of the class. + /// + /// The in-memory bitmap file. + /// The offset of the line to read. + /// The count of bits that may be read (i. e. the width of the image for normal usage). + internal BitReader(byte[] imageBits, uint bytesFileOffset, uint bits) + { + _imageBits = imageBits; + _bytesFileOffset = bytesFileOffset; + _bitsTotal = bits; + _bytesOffsetRead = bytesFileOffset; + _buffer = imageBits[_bytesOffsetRead]; + _bitsInBuffer = 8; + } + + /// + /// Sets the position within the line (needed for 2D encoding). + /// + /// The new position. + internal void SetPosition(uint position) + { + _bytesOffsetRead = _bytesFileOffset + (position >> 3); + _buffer = _imageBits[_bytesOffsetRead]; + _bitsInBuffer = 8 - (position & 0x07); + } + + /// + /// Gets a single bit at the specified position. + /// + /// The position. + /// True if bit is set. + internal bool GetBit(uint position) + { + if (position >= _bitsTotal) + return false; + SetPosition(position); + uint dummy; + return (PeekByte(out dummy) & 0x80) > 0; + } + + /// + /// Returns the bits that are in the buffer (without changing the position). + /// Data is MSB aligned. + /// + /// The count of bits that were returned (1 through 8). + /// The MSB aligned bits from the buffer. + internal byte PeekByte(out uint bits) + { + // TODO: try to make this faster! + if (_bitsInBuffer == 8) + { + bits = 8; + return _buffer; + } + bits = _bitsInBuffer; + return (byte)(_buffer << (int)(8 - _bitsInBuffer)); + } + + /// + /// Moves the buffer to the next byte. + /// + internal void NextByte() + { + _buffer = _imageBits[++_bytesOffsetRead]; + _bitsInBuffer = 8; + } + + /// + /// "Removes" (eats) bits from the buffer. + /// + /// The count of bits that were processed. + internal void SkipBits(uint bits) + { + Debug.Assert(bits <= _bitsInBuffer, "Buffer underrun"); + if (bits == _bitsInBuffer) + { + NextByte(); + return; + } + _bitsInBuffer -= bits; + } + } + + /// + /// A helper class for writing groups of bits into an array of bytes. + /// + class BitWriter + { + /// + /// Initializes a new instance of the class. + /// + /// The byte array to be written to. + internal BitWriter(ref byte[] imageData) + { + _imageData = imageData; + } + + /// + /// Writes the buffered bits into the byte array. + /// + internal void FlushBuffer() + { + if (_bitsInBuffer > 0) + { + uint bits = 8 - _bitsInBuffer; + WriteBits(0, bits); + } + } + + /// + /// Masks for n bits in a byte (with n = 0 through 8). + /// + static readonly uint[] masks = { 0, 1, 3, 7, 15, 31, 63, 127, 255 }; + + /// + /// Writes bits to the byte array. + /// + /// The bits to be written (LSB aligned). + /// The count of bits. + internal void WriteBits(uint value, uint bits) + { +#if true + // TODO: Try to make this faster! + + // If we have to write more bits than fit into the buffer, we fill + // the buffer and call the same routine recursively for the rest. +#if USE_GOTO + // Use GOTO instead of end recursion: (is this faster?) + SimulateRecursion: +#endif + if (bits + _bitsInBuffer > 8) + { + // We can't add all bits this time. + uint bitsNow = 8 - _bitsInBuffer; + uint bitsRemainder = bits - bitsNow; + WriteBits(value >> (int)(bitsRemainder), bitsNow); // that fits +#if USE_GOTO + bits = bitsRemainder; + goto SimulateRecursion; +#else + WriteBits(value, bitsRemainder); + return; +#endif + } + + _buffer = (_buffer << (int)bits) + (value & masks[bits]); + _bitsInBuffer += bits; + + if (_bitsInBuffer == 8) + { + // The line below will sometimes throw a System.IndexOutOfRangeException while PDFsharp tries different formats for monochrome bitmaps (exception occurs if CCITT encoding requires more space than an uncompressed bitmap). + _imageData[_bytesOffsetWrite] = (byte)_buffer; + _bitsInBuffer = 0; + ++_bytesOffsetWrite; + } +#else + // Simple implementation writing bit by bit: + int mask = 1 << (int)(bits - 1); + for (int b = 0; b < bits; ++b) + { + if ((value & mask) != 0) + buffer = (buffer << 1) + 1; + else + buffer = buffer << 1; + ++bitsInBuffer; + mask /= 2; + if (bitsInBuffer == 8) + { + imageData[bytesOffsetWrite] = (byte)buffer; + bitsInBuffer = 0; + ++bytesOffsetWrite; + } + } +#endif + } + + /// + /// Writes a line from a look-up table. + /// A "line" in the table are two integers, one containing the values, one containing the bit count. + /// + internal void WriteTableLine(uint[] table, uint line) + { + uint value = table[line * 2]; + uint bits = table[line * 2 + 1]; + WriteBits(value, bits); + } + + [Obsolete] + internal void WriteEOL() + { + // Not needed for PDF. + WriteTableLine(PdfImage.WhiteMakeUpCodes, 40); + } + + /// + /// Flushes the buffer and returns the count of bytes written to the array. + /// + internal int BytesWritten() + { + FlushBuffer(); + return _bytesOffsetWrite; + } + + int _bytesOffsetWrite; + readonly byte[] _imageData; + uint _buffer; + uint _bitsInBuffer; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.cs new file mode 100644 index 00000000..37191cc3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImage.cs @@ -0,0 +1,1683 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// Thomas Hvel +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +#if CORE +using System.Drawing.Imaging; +#endif +#if GDI +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media.Imaging; +#endif +using PdfSharp.Drawing; +using PdfSharp.Drawing.Internal; +using PdfSharp.Pdf.Filters; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an image. + /// + public sealed partial class PdfImage : PdfXObject + { + /// + /// Initializes a new instance of PdfImage from an XImage. + /// + public PdfImage(PdfDocument document, XImage image) + : base(document) + { + Elements.SetName(Keys.Type, "/XObject"); + Elements.SetName(Keys.Subtype, "/Image"); + + _image = image; + +#if !SILVERLIGHT + ////// TODO: identify images used multiple times. If the image already exists use the same XRef. + ////_defaultName = PdfImageTable.NextImageName; + + switch (_image.Format.Guid.ToString("B").ToUpper()) + { + // Pdf supports Jpeg, therefore we can write what we've read: + case "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}": //XImageFormat.Jpeg + InitializeJpeg(); + break; + + // All other image formats are converted to PDF bitmaps: + case "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}": //XImageFormat.Png + case "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}": //XImageFormat.Gif + case "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}": //XImageFormat.Tiff + case "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}": //XImageFormat.Icon + // TODO: possible optimization for PNG (do not decompress/recompress)??? + // TODO: try Jpeg for size optimization??? + InitializeNonJpeg(); + break; + + case "{84570158-DBF0-4C6B-8368-62D6A3CA76E0}": //XImageFormat.Pdf: + Debug.Assert(false, "XPdfForm not expected here."); + break; + + default: + Debug.Assert(false, "Unexpected image type."); + break; + } +#else + InitializeAg(); +#endif + } + +#if SILVERLIGHT + private void InitializeAg() + { + ReadTrueColorMemoryBitmapAg(3, 8, true); + } + + private void ReadTrueColorMemoryBitmapAg(int components, int bits, bool hasAlpha) + { + int pdfVersion = Owner.Version; + MemoryStream memory = new MemoryStream(); + + WriteableBitmap bitmap = null; + if (_image._wpfImage is WriteableBitmap) + bitmap = (WriteableBitmap)_image._wpfImage; + else if (_image._wpfImage is BitmapImage) + bitmap = new WriteableBitmap(_image._wpfImage); + + if (bitmap != null) + { + int height = _image.PixelHeight; + int width = _image.PixelWidth; + + int logicalComponents = components; + if (components == 4) + logicalComponents = 3; + + byte[] imageData = new byte[components * width * height]; + + bool hasMask = false; + bool hasAlphaMask = false; + byte[] alphaMask = hasAlpha ? new byte[width * height] : null; + MonochromeMask mask = hasAlpha ? new MonochromeMask(width, height) : null; + + int nOffsetRead = 0; + if (logicalComponents == 3) + { + for (int y = 0; y < height; ++y) + { + int nOffsetWrite = 3 * y * width; // 3*(height - 1 - y)*width; + int nOffsetWriteAlpha = 0; + if (hasAlpha) + { + mask.StartLine(y); + nOffsetWriteAlpha = y * width; // (height - 1 - y) * width; + } + + for (int x = 0; x < width; ++x) + { + uint pixel = (uint)bitmap.Pixels[nOffsetRead]; + imageData[nOffsetWrite] = (byte)(pixel >> 16); + imageData[nOffsetWrite + 1] = (byte)(pixel >> 8); + imageData[nOffsetWrite + 2] = (byte)(pixel); + if (hasAlpha) + { + byte pel = (byte)(pixel >> 24); + mask.AddPel(pel); + alphaMask[nOffsetWriteAlpha] = pel; + if (!hasMask || !hasAlphaMask) + { + if (pel != 255) + { + hasMask = true; + if (pel != 0) + hasAlphaMask = true; + } + } + ++nOffsetWriteAlpha; + } + //nOffsetRead += hasAlpha ? 4 : components; + ++nOffsetRead; + nOffsetWrite += 3; + } + //nOffsetRead = 4*((nOffsetRead + 3)/4); // Align to 32 bit boundary + } + } + else if (components == 1) + { + // Grayscale + throw new NotImplementedException("Image format not supported (grayscales)."); + } + + FlateDecode fd = new FlateDecode(); + if (hasMask) + { + // monochrome mask is either sufficient or + // provided for compatibility with older reader versions + byte[] maskDataCompressed = fd.Encode(mask.MaskData, _document.Options.FlateEncodeMode); + PdfDictionary pdfMask = new PdfDictionary(_document); + pdfMask.Elements.SetName(Keys.Type, "/XObject"); + pdfMask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(pdfMask); + pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask); + pdfMask.Elements[Keys.Length] = new PdfInteger(maskDataCompressed.Length); + pdfMask.Elements[Keys.Filter] = new PdfName("/FlateDecode"); + pdfMask.Elements[Keys.Width] = new PdfInteger(width); + pdfMask.Elements[Keys.Height] = new PdfInteger(height); + pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1); + pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true); + Elements[Keys.Mask] = pdfMask.Reference; + } + if (hasMask && hasAlphaMask && pdfVersion >= 14) + { + // The image provides an alpha mask (requires Arcrobat 5.0 or higher) + byte[] alphaMaskCompressed = fd.Encode(alphaMask, _document.Options.FlateEncodeMode); + PdfDictionary smask = new PdfDictionary(_document); + smask.Elements.SetName(Keys.Type, "/XObject"); + smask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(smask); + smask.Stream = new PdfStream(alphaMaskCompressed, smask); + smask.Elements[Keys.Length] = new PdfInteger(alphaMaskCompressed.Length); + smask.Elements[Keys.Filter] = new PdfName("/FlateDecode"); + smask.Elements[Keys.Width] = new PdfInteger(width); + smask.Elements[Keys.Height] = new PdfInteger(height); + smask.Elements[Keys.BitsPerComponent] = new PdfInteger(8); + smask.Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + Elements[Keys.SMask] = smask.Reference; + } + + byte[] imageDataCompressed = fd.Encode(imageData, _document.Options.FlateEncodeMode); + + Stream = new PdfStream(imageDataCompressed, this); + Elements[Keys.Length] = new PdfInteger(imageDataCompressed.Length); + Elements[Keys.Filter] = new PdfName("/FlateDecode"); + Elements[Keys.Width] = new PdfInteger(width); + Elements[Keys.Height] = new PdfInteger(height); + Elements[Keys.BitsPerComponent] = new PdfInteger(8); + // TODO: CMYK + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + } + } +#endif + + /// + /// Gets the underlying XImage object. + /// + public XImage Image + { + get { return _image; } + } + + readonly XImage _image; + + /// + /// Returns 'Image'. + /// + public override string ToString() + { + return "Image"; + } + + /// + /// Creates the keys for a JPEG image. + /// + void InitializeJpeg() + { + // PDF supports JPEG, so there's not much to be done. + MemoryStream memory = null; + // Close the MemoryStream if we create it. + bool ownMemory = false; + + byte[] imageBits = null; + int streamLength = 0; + +#if CORE || GDI || WPF + if (_image._importedImage != null) + { + ImageDataDct idd = (ImageDataDct)_image._importedImage.ImageData; + imageBits = idd.Data; + streamLength = idd.Length; + } +#endif + +#if CORE || GDI + if (_image._importedImage == null) + { + if (!_image._path.StartsWith("*")) + { + // Image does not come from a stream, so we have the path to the file - just use the path. + // If the image was modified in memory, those changes will be lost and the original image, as it was read from the file, will be added to the PDF. + using (FileStream sourceFile = File.OpenRead(_image._path)) + { + int count; + byte[] buffer = new byte[8192]; + memory = new MemoryStream((int)sourceFile.Length); + ownMemory = true; + do + { + count = sourceFile.Read(buffer, 0, buffer.Length); + // memory.Write(buffer, 0, buffer.Length); + memory.Write(buffer, 0, count); + } while (count > 0); + } + } + else + { + memory = new MemoryStream(); + ownMemory = true; + // If we have a stream, copy data from the stream. + if (_image._stream != null && _image._stream.CanSeek) + { + Stream stream = _image._stream; + stream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[32 * 1024]; // 32K buffer. + int bytesRead; + while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) + { + memory.Write(buffer, 0, bytesRead); + } + } + else + { +#if CORE_WITH_GDI + // No stream, no filename, get image data. + // Save the image to a memory stream. + _image._gdiImage.Save(memory, ImageFormat.Jpeg); +#endif + } + } + + if ((int)memory.Length == 0) + { + Debug.Assert(false, "Internal error? JPEG image, but file not found!"); + } + } +#endif +#if WPF + // AGHACK + //string filename = XImage.GetImageFilename(image._wpfImage); + //if (XImage.ReadJpegFile(filename, -1, ref imageBits)) + //{ + // streamLength = imageBits.Length; + //} + //else + // imageBits = null; +#if !SILVERLIGHT + memory = _image.Memory; +#else + memory = new MemoryStream(); + ownMemory = true; +#endif +#endif +#if NETFX_CORE + memory = new MemoryStream(); + ownMemory = true; +#endif + // THHO4THHO Use ImageImporterJPEG here to avoid redundant code. + + if (imageBits == null) + { + streamLength = (int)memory.Length; + imageBits = new byte[streamLength]; + memory.Seek(0, SeekOrigin.Begin); + memory.Read(imageBits, 0, streamLength); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + if (ownMemory) + { +#if UWP || true + memory.Dispose(); +#else + memory.Close(); +#endif + } + } + + bool tryFlateDecode = _document.Options.UseFlateDecoderForJpegImages == PdfUseFlateDecoderForJpegImages.Automatic; + bool useFlateDecode = _document.Options.UseFlateDecoderForJpegImages == PdfUseFlateDecoderForJpegImages.Always; + + FlateDecode fd = new FlateDecode(); + byte[] imageDataCompressed = (useFlateDecode || tryFlateDecode) ? fd.Encode(imageBits, _document.Options.FlateEncodeMode) : null; + if (useFlateDecode || tryFlateDecode && imageDataCompressed.Length < imageBits.Length) + { + Stream = new PdfStream(imageDataCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); + PdfArray arrayFilters = new PdfArray(_document); + arrayFilters.Elements.Add(new PdfName("/FlateDecode")); + arrayFilters.Elements.Add(new PdfName("/DCTDecode")); + Elements[PdfStream.Keys.Filter] = arrayFilters; + } + else + { + Stream = new PdfStream(imageBits, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(streamLength); + Elements[PdfStream.Keys.Filter] = new PdfName("/DCTDecode"); + } + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + Elements[Keys.Width] = new PdfInteger(_image.PixelWidth); + Elements[Keys.Height] = new PdfInteger(_image.PixelHeight); + Elements[Keys.BitsPerComponent] = new PdfInteger(8); + +#if CORE || GDI || WPF + if (_image._importedImage != null) + { + if (_image._importedImage.Information.ImageFormat == ImageInformation.ImageFormats.JPEGCMYK || + _image._importedImage.Information.ImageFormat == ImageInformation.ImageFormats.JPEGRGBW) + { + // TODO: Test with CMYK JPEG files (so far I only found ImageFlags.ColorSpaceYcck JPEG files ...) + Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK"); + if (_image._importedImage.Information.ImageFormat == ImageInformation.ImageFormats.JPEGRGBW) + Elements["/Decode"] = new PdfLiteral("[1 0 1 0 1 0 1 0]"); // Invert colors from RGBW to CMYK. + } + else if (_image._importedImage.Information.ImageFormat == ImageInformation.ImageFormats.JPEGGRAY) + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + } + } +#endif +#if CORE_WITH_GDI + if (_image._importedImage == null) + { + if ((_image._gdiImage.Flags & ((int)ImageFlags.ColorSpaceCmyk | (int)ImageFlags.ColorSpaceYcck)) != 0) + { + // TODO: Test with CMYK JPEG files (so far I only found ImageFlags.ColorSpaceYcck JPEG files ...) + Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK"); + if ((_image._gdiImage.Flags & (int)ImageFlags.ColorSpaceYcck) != 0) + Elements["/Decode"] = new PdfLiteral("[1 0 1 0 1 0 1 0]"); // Invert colors? Why?? + } + else if ((_image._gdiImage.Flags & (int)ImageFlags.ColorSpaceGray) != 0) + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + } + } +#endif +#if GDI + if (_image._importedImage == null) + { + if ((_image._gdiImage.Flags & ((int)ImageFlags.ColorSpaceCmyk | (int)ImageFlags.ColorSpaceYcck)) != 0) + { + // TODO: Test with CMYK JPEG files (so far I only found ImageFlags.ColorSpaceYcck JPEG files ...) + Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK"); + if ((_image._gdiImage.Flags & (int)ImageFlags.ColorSpaceYcck) != 0) + Elements["/Decode"] = new PdfLiteral("[1 0 1 0 1 0 1 0]"); // Invert colors? Why?? + } + else if ((_image._gdiImage.Flags & (int)ImageFlags.ColorSpaceGray) != 0) + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + } + } +#endif +#if WPF + // TODOSILVERLIGHT +#if !SILVERLIGHT + string pixelFormat = _image._wpfImage.Format.ToString(); +#else + string pixelFormat = "xxx"; +#endif + bool isCmyk = _image.IsCmyk; + bool isGrey = pixelFormat == "Gray8"; + if (isCmyk) + { + // TODO: Test with CMYK JPEG files (so far I only found ImageFlags.ColorSpaceYcck JPEG files ...) + Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK"); + Elements["/Decode"] = new PdfLiteral("[1 0 1 0 1 0 1 0]"); // Invert colors? Why?? + } + else if (isGrey) + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + } +#endif + } + + /// + /// Creates the keys for a FLATE image. + /// + void InitializeNonJpeg() + { +#if CORE || GDI || WPF + if (_image._importedImage != null) + { + switch (_image._importedImage.Information.ImageFormat) + { + case ImageInformation.ImageFormats.RGB24: + CreateTrueColorMemoryBitmap(3, 8, false); + break; + + case ImageInformation.ImageFormats.Palette8: + CreateIndexedMemoryBitmap(8); + break; + + case ImageInformation.ImageFormats.Palette4: + CreateIndexedMemoryBitmap(4); + break; + + case ImageInformation.ImageFormats.Palette1: + CreateIndexedMemoryBitmap(1); + break; + + default: + throw new NotImplementedException("Image format not supported."); + } + return; + } +#endif + +#if (CORE_WITH_GDI || GDI) && !WPF + switch (_image._gdiImage.PixelFormat) + { + case PixelFormat.Format24bppRgb: + ReadTrueColorMemoryBitmap(3, 8, false); + break; + + case PixelFormat.Format32bppRgb: + ReadTrueColorMemoryBitmap(4, 8, false); + break; + + case PixelFormat.Format32bppArgb: + case PixelFormat.Format32bppPArgb: + ReadTrueColorMemoryBitmap(3, 8, true); + break; + + case PixelFormat.Format8bppIndexed: + ReadIndexedMemoryBitmap(8); + break; + + case PixelFormat.Format4bppIndexed: + ReadIndexedMemoryBitmap(4); + break; + + case PixelFormat.Format1bppIndexed: + ReadIndexedMemoryBitmap(1); + break; + + default: +#if DEBUGxxx + image.image.Save("$$$.bmp", ImageFormat.Bmp); +#endif + throw new NotImplementedException("Image format not supported."); + } +#endif +#if WPF // && !GDI +#if !SILVERLIGHT + string format = _image._wpfImage.Format.ToString(); +#else + string format = "Bgr24"; +#endif + switch (format) + { + case "Bgr24": //Format24bppRgb: + ReadTrueColorMemoryBitmap(3, 8, false); + break; + + //case .PixelFormat.Format32bppRgb: + // ReadTrueColorMemoryBitmap(4, 8, false); + // break; + + case "Bgra32": //PixelFormat.Format32bppArgb: + //case PixelFormat.Format32bppPArgb: + ReadTrueColorMemoryBitmap(3, 8, true); + break; + + case "Bgr32": + ReadTrueColorMemoryBitmap(4, 8, false); + break; + + case "Pbgra32": + ReadTrueColorMemoryBitmap(3, 8, true); + break; + + case "Indexed8": //Format8bppIndexed: + case "Gray8": + ReadIndexedMemoryBitmap(8); + break; + + case "Indexed4": //Format4bppIndexed: + case "Gray4": + ReadIndexedMemoryBitmap(4); + break; + + case "Indexed2": + ReadIndexedMemoryBitmap(2); + break; + + case "Indexed1": //Format1bppIndexed: + case "BlackWhite": //Format1bppIndexed: + ReadIndexedMemoryBitmap(1); + break; + + default: +#if DEBUGxxx + image.image.Save("$$$.bmp", ImageFormat.Bmp); +#endif + throw new NotImplementedException("Image format \"" + format + "\" not supported."); + } +#endif + } + +#if CORE || GDI || WPF + private void CreateIndexedMemoryBitmap(int bits) + { + ImageDataBitmap idb = (ImageDataBitmap)_image._importedImage.ImageData; + ImageInformation ii = _image._importedImage.Information; + + int pdfVersion = Owner.Version; + int firstMaskColor = -1, lastMaskColor = -1; + bool segmentedColorMask = idb.SegmentedColorMask; + + { + + FlateDecode fd = new FlateDecode(); + if (firstMaskColor != -1 && + lastMaskColor != -1) + { + // Color mask requires Reader 4.0 or higher. + if (!segmentedColorMask && pdfVersion >= 13 && !idb.IsGray) + { + PdfArray array = new PdfArray(_document); + array.Elements.Add(new PdfInteger(firstMaskColor)); + array.Elements.Add(new PdfInteger(lastMaskColor)); + Elements[Keys.Mask] = array; + } + else + { + // Monochrome mask. + byte[] maskDataCompressed = fd.Encode(idb.BitmapMask, _document.Options.FlateEncodeMode); + PdfDictionary pdfMask = new PdfDictionary(_document); + pdfMask.Elements.SetName(Keys.Type, "/XObject"); + pdfMask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(pdfMask); + pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask); + pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length); + pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + pdfMask.Elements[Keys.Width] = new PdfInteger((int)ii.Width); + pdfMask.Elements[Keys.Height] = new PdfInteger((int)ii.Height); + pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1); + pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true); + Elements[Keys.Mask] = pdfMask.Reference; + } + } + + byte[] imageDataCompressed = fd.Encode(idb.Data, _document.Options.FlateEncodeMode); + byte[] imageDataFaxCompressed = idb.DataFax != null ? fd.Encode(idb.DataFax, _document.Options.FlateEncodeMode) : null; + + bool usesCcittEncoding = false; + if (idb.DataFax != null && + (idb.LengthFax < imageDataCompressed.Length || + imageDataFaxCompressed.Length < imageDataCompressed.Length)) + { + // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode). + usesCcittEncoding = true; + + if (idb.LengthFax < imageDataCompressed.Length) + { + Stream = new PdfStream(idb.DataFax, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(idb.LengthFax); + Elements[PdfStream.Keys.Filter] = new PdfName("/CCITTFaxDecode"); + PdfDictionary dictionary = new PdfDictionary(); + if (idb.K != 0) + dictionary.Elements.Add("/K", new PdfInteger(idb.K)); + if (idb.IsBitonal < 0) + dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true)); + dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false)); + dictionary.Elements.Add("/Columns", new PdfInteger((int)ii.Width)); + dictionary.Elements.Add("/Rows", new PdfInteger((int)ii.Height)); + Elements[PdfStream.Keys.DecodeParms] = dictionary; + } + else + { + Stream = new PdfStream(imageDataFaxCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length); + PdfArray arrayFilters = new PdfArray(_document); + arrayFilters.Elements.Add(new PdfName("/FlateDecode")); + arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode")); + Elements[PdfStream.Keys.Filter] = arrayFilters; + PdfArray arrayDecodeParms = new PdfArray(_document); + + PdfDictionary dictFlateDecodeParms = new PdfDictionary(); + + PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary(); + if (idb.K != 0) + dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(idb.K)); + if (idb.IsBitonal < 0) + dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true)); + dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false)); + dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger((int)ii.Width)); + dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger((int)ii.Height)); + + arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"? + arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms); + Elements[PdfStream.Keys.DecodeParms] = arrayDecodeParms; + } + } + else + { + // /FlateDecode creates the smaller file (or no monochrome bitmap). + Stream = new PdfStream(imageDataCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); + Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + } + + Elements[Keys.Width] = new PdfInteger((int)ii.Width); + Elements[Keys.Height] = new PdfInteger((int)ii.Height); + Elements[Keys.BitsPerComponent] = new PdfInteger(bits); + // TODO: CMYK + + // CCITT encoding: we need color palette for isBitonal == 0. + // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales. + if ((usesCcittEncoding && idb.IsBitonal == 0) || + (!usesCcittEncoding && idb.IsBitonal <= 0 && !idb.IsGray)) + { + PdfDictionary colorPalette = null; + colorPalette = new PdfDictionary(_document); + byte[] packedPaletteData = idb.PaletteDataLength >= 48 ? fd.Encode(idb.PaletteData, _document.Options.FlateEncodeMode) : null; // don't compress small palettes + if (packedPaletteData != null && packedPaletteData.Length + 20 < idb.PaletteDataLength) // +20: compensate for the overhead (estimated value) + { + // Create compressed color palette. + colorPalette.CreateStream(packedPaletteData); + colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(packedPaletteData.Length); + colorPalette.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + } + else + { + // Create uncompressed color palette. + colorPalette.CreateStream(idb.PaletteData); + colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(idb.PaletteDataLength); + } + Owner._irefTable.Add(colorPalette); + + PdfArray arrayColorSpace = new PdfArray(_document); + arrayColorSpace.Elements.Add(new PdfName("/Indexed")); + arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB")); + arrayColorSpace.Elements.Add(new PdfInteger((int)ii.ColorsUsed - 1)); + arrayColorSpace.Elements.Add(colorPalette.Reference); + Elements[Keys.ColorSpace] = arrayColorSpace; + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + } + } + + private void CreateTrueColorMemoryBitmap(int components, int bits, bool hasAlpha) + { + int pdfVersion = Owner.Version; + FlateDecode fd = new FlateDecode(); + ImageDataBitmap idb = (ImageDataBitmap)_image._importedImage.ImageData; + ImageInformation ii = _image._importedImage.Information; + bool hasMask = idb.AlphaMaskLength > 0 || idb.BitmapMaskLength > 0; + bool hasAlphaMask = idb.AlphaMaskLength > 0; + + if (hasMask) + { + // Monochrome mask is either sufficient or + // provided for compatibility with older reader versions. + byte[] maskDataCompressed = fd.Encode(idb.BitmapMask, _document.Options.FlateEncodeMode); + PdfDictionary pdfMask = new PdfDictionary(_document); + pdfMask.Elements.SetName(Keys.Type, "/XObject"); + pdfMask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(pdfMask); + pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask); + pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length); + pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + pdfMask.Elements[Keys.Width] = new PdfInteger((int)ii.Width); + pdfMask.Elements[Keys.Height] = new PdfInteger((int)ii.Height); + pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1); + pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true); + Elements[Keys.Mask] = pdfMask.Reference; + } + if (hasMask && hasAlphaMask && pdfVersion >= 14) + { + // The image provides an alpha mask (requires Arcrobat 5.0 or higher). + byte[] alphaMaskCompressed = fd.Encode(idb.AlphaMask, _document.Options.FlateEncodeMode); + PdfDictionary smask = new PdfDictionary(_document); + smask.Elements.SetName(Keys.Type, "/XObject"); + smask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(smask); + smask.Stream = new PdfStream(alphaMaskCompressed, smask); + smask.Elements[PdfStream.Keys.Length] = new PdfInteger(alphaMaskCompressed.Length); + smask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + smask.Elements[Keys.Width] = new PdfInteger((int)ii.Width); + smask.Elements[Keys.Height] = new PdfInteger((int)ii.Height); + smask.Elements[Keys.BitsPerComponent] = new PdfInteger(8); + smask.Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + Elements[Keys.SMask] = smask.Reference; + } + + byte[] imageDataCompressed = fd.Encode(idb.Data, _document.Options.FlateEncodeMode); + + Stream = new PdfStream(imageDataCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); + Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + Elements[Keys.Width] = new PdfInteger((int)ii.Width); + Elements[Keys.Height] = new PdfInteger((int)ii.Height); + Elements[Keys.BitsPerComponent] = new PdfInteger(8); + // TODO: CMYK + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + } +#endif + + private static int ReadWord(byte[] ab, int offset) + { + return ab[offset] + 256 * ab[offset + 1]; + } + + private static int ReadDWord(byte[] ab, int offset) + { + return ReadWord(ab, offset) + 0x10000 * ReadWord(ab, offset + 2); + } + + /// + /// Reads images that are returned from GDI+ without color palette. + /// + /// 4 (32bpp RGB), 3 (24bpp RGB, 32bpp ARGB) + /// 8 + /// true (ARGB), false (RGB) + private void ReadTrueColorMemoryBitmap(int components, int bits, bool hasAlpha) + { +#if DEBUG_ + image.image.Save("$$$.bmp", ImageFormat.Bmp); +#endif + int pdfVersion = Owner.Version; + MemoryStream memory = new MemoryStream(); +#if CORE_WITH_GDI + _image._gdiImage.Save(memory, ImageFormat.Bmp); +#endif +#if GDI + _image._gdiImage.Save(memory, ImageFormat.Bmp); +#endif +#if WPF +#if !SILVERLIGHT + BmpBitmapEncoder encoder = new BmpBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(_image._wpfImage)); + encoder.Save(memory); +#else + // AGHACK + GetType(); +#endif +#endif + // THHO4THHO Use ImageImporterBMP here to avoid redundant code. + + int streamLength = (int)memory.Length; + Debug.Assert(streamLength > 0, "Bitmap image encoding failed."); + if (streamLength > 0) + { +#if !NETFX_CORE && !UWP + // THHO4STLA: available with wrt, but not with wrt81. + // Note: imageBits.Length can be larger than streamLength. Do not use these extra bytes! + byte[] imageBits = memory.GetBuffer(); +#elif NETFX_CORE + byte[] imageBits = new byte[streamLength]; + memory.Seek(0, SeekOrigin.Begin); + memory.Read(imageBits, 0, streamLength); + memory.Close(); +#elif UWP + byte[] imageBits = new byte[streamLength]; + memory.Seek(0, SeekOrigin.Begin); + memory.Read(imageBits, 0, streamLength); + memory.Dispose(); +#endif + + int height = _image.PixelHeight; + int width = _image.PixelWidth; + + // TODO: we could define structures for + // BITMAPFILEHEADER + // { BITMAPINFO } + // BITMAPINFOHEADER + // to avoid ReadWord and ReadDWord ... (but w/o pointers this doesn't help much) + + if (ReadWord(imageBits, 0) != 0x4d42 || // "BM" + ReadDWord(imageBits, 2) != streamLength || + ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER + ReadDWord(imageBits, 18) != width || + ReadDWord(imageBits, 22) != height) + { + throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format"); + } + if (ReadWord(imageBits, 26) != 1 || + (!hasAlpha && ReadWord(imageBits, 28) != components * bits || + hasAlpha && ReadWord(imageBits, 28) != (components + 1) * bits) || + ReadDWord(imageBits, 30) != 0) + { + throw new NotImplementedException("ReadTrueColorMemoryBitmap: unsupported format #2"); + } + + int nFileOffset = ReadDWord(imageBits, 10); + int logicalComponents = components; + if (components == 4) + logicalComponents = 3; + + byte[] imageData = new byte[components * width * height]; + + bool hasMask = false; + bool hasAlphaMask = false; + byte[] alphaMask = hasAlpha ? new byte[width * height] : null; + MonochromeMask mask = hasAlpha ? + new MonochromeMask(width, height) : null; + + int nOffsetRead = 0; + if (logicalComponents == 3) + { + for (int y = 0; y < height; ++y) + { + int nOffsetWrite = 3 * (height - 1 - y) * width; + int nOffsetWriteAlpha = 0; + if (hasAlpha) + { + mask.StartLine(y); + nOffsetWriteAlpha = (height - 1 - y) * width; + } + + for (int x = 0; x < width; ++x) + { + imageData[nOffsetWrite] = imageBits[nFileOffset + nOffsetRead + 2]; + imageData[nOffsetWrite + 1] = imageBits[nFileOffset + nOffsetRead + 1]; + imageData[nOffsetWrite + 2] = imageBits[nFileOffset + nOffsetRead]; + if (hasAlpha) + { + mask.AddPel(imageBits[nFileOffset + nOffsetRead + 3]); + alphaMask[nOffsetWriteAlpha] = imageBits[nFileOffset + nOffsetRead + 3]; + if (!hasMask || !hasAlphaMask) + { + if (imageBits[nFileOffset + nOffsetRead + 3] != 255) + { + hasMask = true; + if (imageBits[nFileOffset + nOffsetRead + 3] != 0) + hasAlphaMask = true; + } + } + ++nOffsetWriteAlpha; + } + nOffsetRead += hasAlpha ? 4 : components; + nOffsetWrite += 3; + } + nOffsetRead = 4 * ((nOffsetRead + 3) / 4); // Align to 32 bit boundary + } + } + else if (components == 1) + { + // Grayscale + throw new NotImplementedException("Image format not supported (grayscales)."); + } + + FlateDecode fd = new FlateDecode(); + if (hasMask) + { + // Monochrome mask is either sufficient or + // provided for compatibility with older reader versions. + byte[] maskDataCompressed = fd.Encode(mask.MaskData, _document.Options.FlateEncodeMode); + PdfDictionary pdfMask = new PdfDictionary(_document); + pdfMask.Elements.SetName(Keys.Type, "/XObject"); + pdfMask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(pdfMask); + pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask); + pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length); + pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + pdfMask.Elements[Keys.Width] = new PdfInteger(width); + pdfMask.Elements[Keys.Height] = new PdfInteger(height); + pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1); + pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true); + Elements[Keys.Mask] = pdfMask.Reference; + } + if (hasMask && hasAlphaMask && pdfVersion >= 14) + { + // The image provides an alpha mask (requires Arcrobat 5.0 or higher). + byte[] alphaMaskCompressed = fd.Encode(alphaMask, _document.Options.FlateEncodeMode); + PdfDictionary smask = new PdfDictionary(_document); + smask.Elements.SetName(Keys.Type, "/XObject"); + smask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(smask); + smask.Stream = new PdfStream(alphaMaskCompressed, smask); + smask.Elements[PdfStream.Keys.Length] = new PdfInteger(alphaMaskCompressed.Length); + smask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + smask.Elements[Keys.Width] = new PdfInteger(width); + smask.Elements[Keys.Height] = new PdfInteger(height); + smask.Elements[Keys.BitsPerComponent] = new PdfInteger(8); + smask.Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + Elements[Keys.SMask] = smask.Reference; + } + + byte[] imageDataCompressed = fd.Encode(imageData, _document.Options.FlateEncodeMode); + + Stream = new PdfStream(imageDataCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); + Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + Elements[Keys.Width] = new PdfInteger(width); + Elements[Keys.Height] = new PdfInteger(height); + Elements[Keys.BitsPerComponent] = new PdfInteger(8); + // TODO: CMYK + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + } + } + + /* BITMAPINFOHEADER struct and byte offsets: + typedef struct tagBITMAPINFOHEADER{ + DWORD biSize; // 14 + LONG biWidth; // 18 + LONG biHeight; // 22 + WORD biPlanes; // 26 + WORD biBitCount; // 28 + DWORD biCompression; // 30 + DWORD biSizeImage; // 34 + LONG biXPelsPerMeter; // 38 + LONG biYPelsPerMeter; // 42 + DWORD biClrUsed; // 46 + DWORD biClrImportant; // 50 + } BITMAPINFOHEADER, *PBITMAPINFOHEADER; + */ + + private void ReadIndexedMemoryBitmap(int bits) + { + int pdfVersion = Owner.Version; + int firstMaskColor = -1, lastMaskColor = -1; + bool segmentedColorMask = false; + + MemoryStream memory = new MemoryStream(); +#if CORE_WITH_GDI + _image._gdiImage.Save(memory, ImageFormat.Bmp); +#endif +#if GDI + _image._gdiImage.Save(memory, ImageFormat.Bmp); +#endif +#if WPF +#if !SILVERLIGHT + BmpBitmapEncoder encoder = new BmpBitmapEncoder(); + //if (!_image._path.StartsWith("*")) + // encoder.Frames.Add(BitmapFrame.Create(new Uri(_image._path), BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)); + //else + encoder.Frames.Add(BitmapFrame.Create(_image._wpfImage)); + encoder.Save(memory); +#else + // AGHACK + GetType(); +#endif +#endif + // THHO4THHO Use ImageImporterBMP here to avoid redundant code. + + int streamLength = (int)memory.Length; + Debug.Assert(streamLength > 0, "Bitmap image encoding failed."); + if (streamLength > 0) + { + byte[] imageBits = new byte[streamLength]; + memory.Seek(0, SeekOrigin.Begin); + memory.Read(imageBits, 0, streamLength); +#if !UWP + memory.Close(); +#else + memory.Dispose(); +#endif + + int height = _image.PixelHeight; + int width = _image.PixelWidth; + + if (ReadWord(imageBits, 0) != 0x4d42 || // "BM" + ReadDWord(imageBits, 2) != streamLength || + ReadDWord(imageBits, 14) != 40 || // sizeof BITMAPINFOHEADER +#if WPF + // TODOWPF: bug with height and width??? With which files??? + ReadDWord(imageBits, 18) != width || + ReadDWord(imageBits, 22) != height) +#else + ReadDWord(imageBits, 18) != width || + ReadDWord(imageBits, 22) != height) +#endif + { + throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format"); + } +#if WPF + // TODOWPF: bug with height and width + width = ReadDWord(imageBits, 18); + height = ReadDWord(imageBits, 22); +#endif + int fileBits = ReadWord(imageBits, 28); + if (fileBits != bits) + { + if (fileBits == 1 || fileBits == 4 || fileBits == 8) + bits = fileBits; + } + + if (ReadWord(imageBits, 26) != 1 || + ReadWord(imageBits, 28) != bits || + ReadDWord(imageBits, 30) != 0) + { + throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #2"); + } + + int bytesFileOffset = ReadDWord(imageBits, 10); + const int bytesColorPaletteOffset = 0x36; // GDI+ always returns Windows bitmaps: sizeof BITMAPFILEHEADER + sizeof BITMAPINFOHEADER + int paletteColors = ReadDWord(imageBits, 46); + if ((bytesFileOffset - bytesColorPaletteOffset) / 4 != paletteColors) + { + throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3"); + } + + MonochromeMask mask = new MonochromeMask(width, height); + + bool isGray = bits == 8 && (paletteColors == 256 || paletteColors == 0); + int isBitonal = 0; // 0: false; >0: true; <0: true (inverted). + byte[] paletteData = new byte[3 * paletteColors]; + for (int color = 0; color < paletteColors; ++color) + { + paletteData[3 * color] = imageBits[bytesColorPaletteOffset + 4 * color + 2]; + paletteData[3 * color + 1] = imageBits[bytesColorPaletteOffset + 4 * color + 1]; + paletteData[3 * color + 2] = imageBits[bytesColorPaletteOffset + 4 * color + 0]; + if (isGray) + isGray = paletteData[3 * color] == paletteData[3 * color + 1] && + paletteData[3 * color] == paletteData[3 * color + 2]; + + if (imageBits[bytesColorPaletteOffset + 4 * color + 3] < 128) + { + // We treat this as transparency. + if (firstMaskColor == -1) + firstMaskColor = color; + if (lastMaskColor == -1 || lastMaskColor == color - 1) + lastMaskColor = color; + if (lastMaskColor != color) + segmentedColorMask = true; + } + //else + //{ + // // We treat this as opacity. + //} + } + + if (bits == 1) + { + if (paletteColors == 0) + isBitonal = 1; + if (paletteColors == 2) + { + if (paletteData[0] == 0 && + paletteData[1] == 0 && + paletteData[2] == 0 && + paletteData[3] == 255 && + paletteData[4] == 255 && + paletteData[5] == 255) + isBitonal = 1; // Black on white + if (paletteData[5] == 0 && + paletteData[4] == 0 && + paletteData[3] == 0 && + paletteData[2] == 255 && + paletteData[1] == 255 && + paletteData[0] == 255) + isBitonal = -1; // White on black + } + } + + bool hasMask = firstMaskColor != -1 && lastMaskColor != -1; + + // NYI: (no sample found where this was required) + // if (segmentedColorMask = true) + // { ... } + + bool isFaxEncoding = false; + byte[] imageData = new byte[((width * bits + 7) / 8) * height]; + byte[] imageDataFax = null; + int k = 0; + + // If fax encoding is allowed, try if fax encoding reduces the size. + if (bits == 1 && _document.Options.EnableCcittCompressionForBilevelImages) + { + // TODO: flag/option? + // We try Group 3 1D and Group 4 (2D) encoding here and keep the smaller byte array. + //byte[] temp = new byte[imageData.Length]; + //int ccittSize = DoFaxEncoding(ref temp, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height); + + // It seems that Group 3 2D encoding never beats both other encodings, therefore we don't call it here. + //byte[] temp2D = new byte[imageData.Length]; + //uint dpiY = (uint)image.VerticalResolution; + //uint kTmp = 0; + //int ccittSize2D = DoFaxEncoding2D((uint)bytesFileOffset, ref temp2D, imageBits, (uint)width, (uint)height, dpiY, out kTmp); + //k = (int) kTmp; + + byte[] tempG4 = new byte[imageData.Length]; + int ccittSizeG4 = DoFaxEncodingGroup4(ref tempG4, imageBits, (uint)bytesFileOffset, (uint)width, (uint)height); + + isFaxEncoding = /*ccittSize > 0 ||*/ ccittSizeG4 > 0; + if (isFaxEncoding) + { + //if (ccittSize == 0) + // ccittSize = 0x7fffffff; + if (ccittSizeG4 == 0) + ccittSizeG4 = 0x7fffffff; + //if (ccittSize <= ccittSizeG4) + //{ + // Array.Resize(ref temp, ccittSize); + // imageDataFax = temp; + // k = 0; + //} + //else + { + Array.Resize(ref tempG4, ccittSizeG4); + imageDataFax = tempG4; + k = -1; + } + } + } + + //if (hasMask) + { + int bytesOffsetRead = 0; + if (bits == 8 || bits == 4 || bits == 1) + { + int bytesPerLine = (width * bits + 7) / 8; + for (int y = 0; y < height; ++y) + { + mask.StartLine(y); + int bytesOffsetWrite = (height - 1 - y) * ((width * bits + 7) / 8); + for (int x = 0; x < bytesPerLine; ++x) + { + if (isGray) + { + // Lookup the gray value from the palette: + imageData[bytesOffsetWrite] = paletteData[3 * imageBits[bytesFileOffset + bytesOffsetRead]]; + } + else + { + // Store the palette index. + imageData[bytesOffsetWrite] = imageBits[bytesFileOffset + bytesOffsetRead]; + } + if (firstMaskColor != -1) + { + int n = imageBits[bytesFileOffset + bytesOffsetRead]; + if (bits == 8) + { + // TODO???: segmentedColorMask == true => bad mask NYI + mask.AddPel((n >= firstMaskColor) && (n <= lastMaskColor)); + } + else if (bits == 4) + { + // TODO???: segmentedColorMask == true => bad mask NYI + int n1 = (n & 0xf0) / 16; + int n2 = (n & 0x0f); + mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor)); + mask.AddPel((n2 >= firstMaskColor) && (n2 <= lastMaskColor)); + } + else if (bits == 1) + { + // TODO???: segmentedColorMask == true => bad mask NYI + for (int bit = 1; bit <= 8; ++bit) + { + int n1 = (n & 0x80) / 128; + mask.AddPel((n1 >= firstMaskColor) && (n1 <= lastMaskColor)); + n *= 2; + } + } + } + bytesOffsetRead += 1; + bytesOffsetWrite += 1; + } + bytesOffsetRead = 4 * ((bytesOffsetRead + 3) / 4); // Align to 32 bit boundary. + } + } + else + { + throw new NotImplementedException("ReadIndexedMemoryBitmap: unsupported format #3"); + } + } + + FlateDecode fd = new FlateDecode(); + if (hasMask) + { + // Color mask requires Reader 4.0 or higher. + if (!segmentedColorMask && pdfVersion >= 13 && !isGray) + { + PdfArray array = new PdfArray(_document); + array.Elements.Add(new PdfInteger(firstMaskColor)); + array.Elements.Add(new PdfInteger(lastMaskColor)); + Elements[Keys.Mask] = array; + } + else + { + // Monochrome mask. + byte[] maskDataCompressed = fd.Encode(mask.MaskData, _document.Options.FlateEncodeMode); + PdfDictionary pdfMask = new PdfDictionary(_document); + pdfMask.Elements.SetName(Keys.Type, "/XObject"); + pdfMask.Elements.SetName(Keys.Subtype, "/Image"); + + Owner._irefTable.Add(pdfMask); + pdfMask.Stream = new PdfStream(maskDataCompressed, pdfMask); + pdfMask.Elements[PdfStream.Keys.Length] = new PdfInteger(maskDataCompressed.Length); + pdfMask.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + pdfMask.Elements[Keys.Width] = new PdfInteger(width); + pdfMask.Elements[Keys.Height] = new PdfInteger(height); + pdfMask.Elements[Keys.BitsPerComponent] = new PdfInteger(1); + pdfMask.Elements[Keys.ImageMask] = new PdfBoolean(true); + Elements[Keys.Mask] = pdfMask.Reference; + } + } + + byte[] imageDataCompressed = fd.Encode(imageData, _document.Options.FlateEncodeMode); + byte[] imageDataFaxCompressed = isFaxEncoding ? fd.Encode(imageDataFax, _document.Options.FlateEncodeMode) : null; + + bool usesCcittEncoding = false; + if (isFaxEncoding && + (imageDataFax.Length < imageDataCompressed.Length || + imageDataFaxCompressed.Length < imageDataCompressed.Length)) + { + // /CCITTFaxDecode creates the smaller file (with or without /FlateDecode). + usesCcittEncoding = true; + + if (imageDataFax.Length < imageDataCompressed.Length) + { + Stream = new PdfStream(imageDataFax, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFax.Length); + Elements[PdfStream.Keys.Filter] = new PdfName("/CCITTFaxDecode"); + PdfDictionary dictionary = new PdfDictionary(); + if (k != 0) + dictionary.Elements.Add("/K", new PdfInteger(k)); + if (isBitonal < 0) + dictionary.Elements.Add("/BlackIs1", new PdfBoolean(true)); + dictionary.Elements.Add("/EndOfBlock", new PdfBoolean(false)); + dictionary.Elements.Add("/Columns", new PdfInteger(width)); + dictionary.Elements.Add("/Rows", new PdfInteger(height)); + //array2.Elements.Add(dictionary); + Elements[PdfStream.Keys.DecodeParms] = dictionary; // array2; + } + else + { + Stream = new PdfStream(imageDataFaxCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataFaxCompressed.Length); + PdfArray arrayFilters = new PdfArray(_document); + arrayFilters.Elements.Add(new PdfName("/FlateDecode")); + arrayFilters.Elements.Add(new PdfName("/CCITTFaxDecode")); + Elements[PdfStream.Keys.Filter] = arrayFilters; + PdfArray arrayDecodeParms = new PdfArray(_document); + + PdfDictionary dictFlateDecodeParms = new PdfDictionary(); + + PdfDictionary dictCcittFaxDecodeParms = new PdfDictionary(); + if (k != 0) + dictCcittFaxDecodeParms.Elements.Add("/K", new PdfInteger(k)); + if (isBitonal < 0) + dictCcittFaxDecodeParms.Elements.Add("/BlackIs1", new PdfBoolean(true)); + dictCcittFaxDecodeParms.Elements.Add("/EndOfBlock", new PdfBoolean(false)); + dictCcittFaxDecodeParms.Elements.Add("/Columns", new PdfInteger(width)); + dictCcittFaxDecodeParms.Elements.Add("/Rows", new PdfInteger(height)); + + arrayDecodeParms.Elements.Add(dictFlateDecodeParms); // How to add the "null object"? + arrayDecodeParms.Elements.Add(dictCcittFaxDecodeParms); + Elements[PdfStream.Keys.DecodeParms] = arrayDecodeParms; + } + } + else + { + // /FlateDecode creates the smaller file (or no monochrome bitmap). + Stream = new PdfStream(imageDataCompressed, this); + Elements[PdfStream.Keys.Length] = new PdfInteger(imageDataCompressed.Length); + Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + } + + Elements[Keys.Width] = new PdfInteger(width); + Elements[Keys.Height] = new PdfInteger(height); + Elements[Keys.BitsPerComponent] = new PdfInteger(bits); + // TODO: CMYK + + // CCITT encoding: we need color palette for isBitonal == 0. + // FlateDecode: we need color palette for isBitonal <= 0 unless we have grayscales. + if ((usesCcittEncoding && isBitonal == 0) || + (!usesCcittEncoding && isBitonal <= 0 && !isGray)) + { + PdfDictionary colorPalette = null; + colorPalette = new PdfDictionary(_document); + byte[] packedPaletteData = paletteData.Length >= 48 ? fd.Encode(paletteData, _document.Options.FlateEncodeMode) : null; // don't compress small palettes + if (packedPaletteData != null && packedPaletteData.Length + 20 < paletteData.Length) // +20: compensate for the overhead (estimated value) + { + // Create compressed color palette. + colorPalette.CreateStream(packedPaletteData); + colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(packedPaletteData.Length); + colorPalette.Elements[PdfStream.Keys.Filter] = new PdfName("/FlateDecode"); + } + else + { + // Create uncompressed color palette. + colorPalette.CreateStream(paletteData); + colorPalette.Elements[PdfStream.Keys.Length] = new PdfInteger(paletteData.Length); + } + Owner._irefTable.Add(colorPalette); + + PdfArray arrayColorSpace = new PdfArray(_document); + arrayColorSpace.Elements.Add(new PdfName("/Indexed")); + arrayColorSpace.Elements.Add(new PdfName("/DeviceRGB")); + arrayColorSpace.Elements.Add(new PdfInteger(paletteColors - 1)); + arrayColorSpace.Elements.Add(colorPalette.Reference); + Elements[Keys.ColorSpace] = arrayColorSpace; + } + else + { + Elements[Keys.ColorSpace] = new PdfName("/DeviceGray"); + } + if (_image.Interpolate) + Elements[Keys.Interpolate] = PdfBoolean.True; + } + } + + /// + /// Common keys for all streams. + /// + public sealed new class Keys : PdfXObject.Keys + { + // ReSharper disable InconsistentNaming + + /// + /// (Optional) The type of PDF object that this dictionary describes; + /// if present, must be XObject for an image XObject. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Type = "/Type"; + + /// + /// (Required) The type of XObject that this dictionary describes; + /// must be Image for an image XObject. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Subtype = "/Subtype"; + + /// + /// (Required) The width of the image, in samples. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Width = "/Width"; + + /// + /// (Required) The height of the image, in samples. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Height = "/Height"; + + /// + /// (Required for images, except those that use the JPXDecode filter; not allowed for image masks) + /// The color space in which image samples are specified; it can be any type of color space except + /// Pattern. If the image uses the JPXDecode filter, this entry is optional: + /// If ColorSpace is present, any color space specifications in the JPEG2000 data are ignored. + /// If ColorSpace is absent, the color space specifications in the JPEG2000 data are used. + /// The Decode array is also ignored unless ImageMask is true. + /// + [KeyInfo(KeyType.NameOrArray | KeyType.Required)] + public const string ColorSpace = "/ColorSpace"; + + /// + /// (Required except for image masks and images that use the JPXDecode filter) + /// The number of bits used to represent each color component. Only a single value may be specified; + /// the number of bits is the same for all color components. Valid values are 1, 2, 4, 8, and + /// (in PDF 1.5) 16. If ImageMask is true, this entry is optional, and if specified, its value + /// must be 1. + /// If the image stream uses a filter, the value of BitsPerComponent must be consistent with the + /// size of the data samples that the filter delivers. In particular, a CCITTFaxDecode or JBIG2Decode + /// filter always delivers 1-bit samples, a RunLengthDecode or DCTDecode filter delivers 8-bit samples, + /// and an LZWDecode or FlateDecode filter delivers samples of a specified size if a predictor function + /// is used. + /// If the image stream uses the JPXDecode filter, this entry is optional and ignored if present. + /// The bit depth is determined in the process of decoding the JPEG2000 image. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string BitsPerComponent = "/BitsPerComponent"; + + /// + /// (Optional; PDF 1.1) The name of a color rendering intent to be used in rendering the image. + /// Default value: the current rendering intent in the graphics state. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Intent = "/Intent"; + + /// + /// (Optional) A flag indicating whether the image is to be treated as an image mask. + /// If this flag is true, the value of BitsPerComponent must be 1 and Mask and ColorSpace should + /// not be specified; unmasked areas are painted using the current nonstroking color. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string ImageMask = "/ImageMask"; + + /// + /// (Optional except for image masks; not allowed for image masks; PDF 1.3) + /// An image XObject defining an image mask to be applied to this image, or an array specifying + /// a range of colors to be applied to it as a color key mask. If ImageMask is true, this entry + /// must not be present. + /// + [KeyInfo(KeyType.StreamOrArray | KeyType.Optional)] + public const string Mask = "/Mask"; + + /// + /// (Optional) An array of numbers describing how to map image samples into the range of values + /// appropriate for the images color space. If ImageMask is true, the array must be either + /// [0 1] or [1 0]; otherwise, its length must be twice the number of color components required + /// by ColorSpace. If the image uses the JPXDecode filter and ImageMask is false, Decode is ignored. + /// Default value: see Decode Arrays. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Decode = "/Decode"; + + /// + /// (Optional) A flag indicating whether image interpolation is to be performed. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string Interpolate = "/Interpolate"; + + /// + /// (Optional; PDF 1.3) An array of alternate image dictionaries for this image. The order of + /// elements within the array has no significance. This entry may not be present in an image + /// XObject that is itself an alternate image. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Alternates = "/Alternates"; + + /// + /// (Optional; PDF 1.4) A subsidiary image XObject defining a soft-mask image to be used as a + /// source of mask shape or mask opacity values in the transparent imaging model. The alpha + /// source parameter in the graphics state determines whether the mask values are interpreted as + /// shape or opacity. If present, this entry overrides the current soft mask in the graphics state, + /// as well as the images Mask entry, if any. (However, the other transparency related graphics + /// state parameters blend mode and alpha constant remain in effect.) If SMask is absent, the + /// image has no associated soft mask (although the current soft mask in the graphics state may + /// still apply). + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string SMask = "/SMask"; + + /// + /// (Optional for images that use the JPXDecode filter, meaningless otherwise; PDF 1.5) + /// A code specifying how soft-mask information encoded with image samples should be used: + /// 0 If present, encoded soft-mask image information should be ignored. + /// 1 The images data stream includes encoded soft-mask values. An application can create + /// a soft-mask image from the information to be used as a source of mask shape or mask + /// opacity in the transparency imaging model. + /// 2 The images data stream includes color channels that have been preblended with a + /// background; the image data also includes an opacity channel. An application can create + /// a soft-mask image with a Matte entry from the opacity channel information to be used as + /// a source of mask shape or mask opacity in the transparency model. If this entry has a + /// nonzero value, SMask should not be specified. + /// Default value: 0. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string SMaskInData = "/SMaskInData"; + + /// + /// (Required in PDF 1.0; optional otherwise) The name by which this image XObject is + /// referenced in the XObject subdictionary of the current resource dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Name = "/Name"; + + /// + /// (Required if the image is a structural content item; PDF 1.3) The integer key of the + /// images entry in the structural parent tree. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string StructParent = "/StructParent"; + + /// + /// (Optional; PDF 1.3; indirect reference preferred) The digital identifier of the images + /// parent Web Capture content set. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string ID = "/ID"; + + /// + /// (Optional; PDF 1.2) An OPI version dictionary for the image. If ImageMask is true, + /// this entry is ignored. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string OPI = "/OPI"; + + /// + /// (Optional; PDF 1.4) A metadata stream containing metadata for the image. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string Metadata = "/Metadata"; + + /// + /// (Optional; PDF 1.5) An optional content group or optional content membership dictionary, + /// specifying the optional content properties for this image XObject. Before the image is + /// processed, its visibility is determined based on this entry. If it is determined to be + /// invisible, the entire image is skipped, as if there were no Do operator to invoke it. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string OC = "/OC"; + + // ReSharper restore InconsistentNaming + } + } + + /// + /// Helper class for creating bitmap masks (8 pels per byte). + /// + class MonochromeMask + { + /// + /// Returns the bitmap mask that will be written to PDF. + /// + public byte[] MaskData + { + get { return _maskData; } + } + private readonly byte[] _maskData; + + /// + /// Creates a bitmap mask. + /// + public MonochromeMask(int sizeX, int sizeY) + { + _sizeX = sizeX; + _sizeY = sizeY; + int byteSize = ((sizeX + 7) / 8) * sizeY; + _maskData = new byte[byteSize]; + StartLine(0); + } + + /// + /// Starts a new line. + /// + public void StartLine(int newCurrentLine) + { + _bitsWritten = 0; + _byteBuffer = 0; + _writeOffset = ((_sizeX + 7) / 8) * (_sizeY - 1 - newCurrentLine); + } + + /// + /// Adds a pel to the current line. + /// + /// + public void AddPel(bool isTransparent) + { + if (_bitsWritten < _sizeX) + { + // Mask: 0: opaque, 1: transparent (default mapping) + if (isTransparent) + _byteBuffer = (_byteBuffer << 1) + 1; + else + _byteBuffer = _byteBuffer << 1; + ++_bitsWritten; + if ((_bitsWritten & 7) == 0) + { + _maskData[_writeOffset] = (byte)_byteBuffer; + ++_writeOffset; + _byteBuffer = 0; + } + else if (_bitsWritten == _sizeX) + { + int n = 8 - (_bitsWritten & 7); + _byteBuffer = _byteBuffer << n; + _maskData[_writeOffset] = (byte)_byteBuffer; + } + } + } + + /// + /// Adds a pel from an alpha mask value. + /// + public void AddPel(int shade) + { + // NYI: dithering. + AddPel(shade < 128); + } + + private readonly int _sizeX; + private readonly int _sizeY; + private int _writeOffset; + private int _byteBuffer; + private int _bitsWritten; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImageTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImageTable.cs new file mode 100644 index 00000000..54285478 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImageTable.cs @@ -0,0 +1,117 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Globalization; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Contains all used images of a document. + /// + internal sealed class PdfImageTable : PdfResourceTable + { + /// + /// Initializes a new instance of this class, which is a singleton for each document. + /// + public PdfImageTable(PdfDocument document) + : base(document) + { } + + /// + /// Gets a PdfImage from an XImage. If no PdfImage already exists, a new one is created. + /// + public PdfImage GetImage(XImage image) + { + ImageSelector selector = image._selector; + if (selector == null) + { + selector = new ImageSelector(image); + image._selector = selector; + } + PdfImage pdfImage; + if (!_images.TryGetValue(selector, out pdfImage)) + { + pdfImage = new PdfImage(Owner, image); + //pdfImage.Document = _document; + Debug.Assert(pdfImage.Owner == Owner); + _images[selector] = pdfImage; + } + return pdfImage; + } + + /// + /// Map from ImageSelector to PdfImage. + /// + readonly Dictionary _images = new Dictionary(); + + /// + /// A collection of information that uniquely identifies a particular PdfImage. + /// + public class ImageSelector + { + /// + /// Initializes a new instance of ImageSelector from an XImage. + /// + public ImageSelector(XImage image) + { + // HACK: implement a way to identify images when they are reused + // TODO 4STLA Implementation that calculates MD5 hashes for images generated for the images can be found here: http://forum.pdfsharp.net/viewtopic.php?p=6959#p6959 + if (image._path == null) + image._path = "*" + Guid.NewGuid().ToString("B"); + + // HACK: just use full path to identify + _path = image._path.ToLowerInvariant(); + } + + public string Path + { + get { return _path; } + set { _path = value; } + } + string _path; + + public override bool Equals(object obj) + { + ImageSelector selector = obj as ImageSelector; + if (selector == null) + return false; + return _path == selector._path; + } + + public override int GetHashCode() + { + return _path.GetHashCode(); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImportedObjectTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImportedObjectTable.cs new file mode 100644 index 00000000..de020192 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfImportedObjectTable.cs @@ -0,0 +1,117 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents the imported objects of an external document. Used to cache objects that are + /// already imported when a PdfFormXObject is added to a page. + /// + internal sealed class PdfImportedObjectTable + { + /// + /// Initializes a new instance of this class with the document the objects are imported from. + /// + public PdfImportedObjectTable(PdfDocument owner, PdfDocument externalDocument) + { + if (owner == null) + throw new ArgumentNullException("owner"); + if (externalDocument == null) + throw new ArgumentNullException("externalDocument"); + _owner = owner; + _externalDocumentHandle = externalDocument.Handle; + _xObjects = new PdfFormXObject[externalDocument.PageCount]; + } + readonly PdfFormXObject[] _xObjects; + + /// + /// Gets the document this table belongs to. + /// + public PdfDocument Owner + { + get { return _owner; } + } + readonly PdfDocument _owner; + + /// + /// Gets the external document, or null, if the external document is garbage collected. + /// + public PdfDocument ExternalDocument + { + get { return _externalDocumentHandle.IsAlive ? _externalDocumentHandle.Target : null; } + } + readonly PdfDocument.DocumentHandle _externalDocumentHandle; + + public PdfFormXObject GetXObject(int pageNumber) + { + return _xObjects[pageNumber - 1]; + } + + public void SetXObject(int pageNumber, PdfFormXObject xObject) + { + _xObjects[pageNumber - 1] = xObject; + } + + /// + /// Indicates whether the specified object is already imported. + /// + public bool Contains(PdfObjectID externalID) + { + return _externalIDs.ContainsKey(externalID.ToString()); + } + + /// + /// Adds a cloned object to this table. + /// + /// The object identifier in the foreign object. + /// The cross reference to the clone of the foreign object, which belongs to + /// this document. In general the clone has a different object identifier. + public void Add(PdfObjectID externalID, PdfReference iref) + { + _externalIDs[externalID.ToString()] = iref; + } + + /// + /// Gets the cloned object that corresponds to the specified external identifier. + /// + public PdfReference this[PdfObjectID externalID] + { + get { return _externalIDs[externalID.ToString()]; } + } + + /// + /// Maps external object identifiers to cross reference entries of the importing document + /// {PdfObjectID -> PdfReference}. + /// + readonly Dictionary _externalIDs = new Dictionary(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfInternals.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfInternals.cs new file mode 100644 index 00000000..a9c57c43 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfInternals.cs @@ -0,0 +1,307 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using System.IO; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Provides access to the internal document data structures. This class prevents the public + /// interfaces from pollution with to much internal functions. + /// + public class PdfInternals // TODO: PdfDocumentInternals... PdfPageInterals etc. + { + internal PdfInternals(PdfDocument document) + { + _document = document; + } + readonly PdfDocument _document; + + /// + /// Gets or sets the first document identifier. + /// + public string FirstDocumentID + { + get { return _document._trailer.GetDocumentID(0); } + set { _document._trailer.SetDocumentID(0, value); } + } + + /// + /// Gets the first document identifier as GUID. + /// + public Guid FirstDocumentGuid + { + get { return GuidFromString(_document._trailer.GetDocumentID(0)); } + } + + /// + /// Gets or sets the second document identifier. + /// + public string SecondDocumentID + { + get { return _document._trailer.GetDocumentID(1); } + set { _document._trailer.SetDocumentID(1, value); } + } + + /// + /// Gets the first document identifier as GUID. + /// + public Guid SecondDocumentGuid + { + get { return GuidFromString(_document._trailer.GetDocumentID(0)); } + } + + Guid GuidFromString(string id) + { + if (id == null || id.Length != 16) + return Guid.Empty; + + StringBuilder guid = new StringBuilder(); + for (int idx = 0; idx < 16; idx++) + guid.AppendFormat("{0:X2}", (byte)id[idx]); + + return new Guid(guid.ToString()); + } + + /// + /// Gets the catalog dictionary. + /// + public PdfCatalog Catalog + { + get { return _document.Catalog; } + } + + /// + /// Gets the ExtGStateTable object. + /// + public PdfExtGStateTable ExtGStateTable + { + get { return _document.ExtGStateTable; } + } + + /// + /// This property is not documented by intention. + /// + // ReSharper disable once InconsistentNaming + public object UAManager // @PDF/UA + { + get { return _document._uaManager; } + } + + /// + /// Returns the object with the specified Identifier, or null, if no such object exists. + /// + public PdfObject GetObject(PdfObjectID objectID) + { + return _document._irefTable[objectID].Value; + } + + /// + /// Maps the specified external object to the substitute object in this document. + /// Returns null if no such object exists. + /// + public PdfObject MapExternalObject(PdfObject externalObject) + { + PdfFormXObjectTable table = _document.FormTable; + PdfImportedObjectTable iot = table.GetImportedObjectTable(externalObject.Owner); + PdfReference reference = iot[externalObject.ObjectID]; + return reference == null ? null : reference.Value; + } + + /// + /// Returns the PdfReference of the specified object, or null, if the object is not in the + /// document's object table. + /// + public static PdfReference GetReference(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + return obj.Reference; + } + + /// + /// Gets the object identifier of the specified object. + /// + public static PdfObjectID GetObjectID(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + return obj.ObjectID; + } + + /// + /// Gets the object number of the specified object. + /// + public static int GetObjectNumber(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + return obj.ObjectNumber; + } + + /// + /// Gets the generation number of the specified object. + /// + public static int GenerationNumber(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + return obj.GenerationNumber; + } + + /// + /// Gets all indirect objects ordered by their object identifier. + /// + public PdfObject[] GetAllObjects() + { + PdfReference[] irefs = _document._irefTable.AllReferences; + int count = irefs.Length; + PdfObject[] objects = new PdfObject[count]; + for (int idx = 0; idx < count; idx++) + objects[idx] = irefs[idx].Value; + return objects; + } + + /// + /// Gets all indirect objects ordered by their object identifier. + /// + [Obsolete("Use GetAllObjects.")] // Properties should not return arrays + public PdfObject[] AllObjects + { + get { return GetAllObjects(); } + } + + /// + /// Creates the indirect object of the specified type, adds it to the document, + /// and returns the object. + /// + public T CreateIndirectObject() where T : PdfObject + { +#if true + T obj = Activator.CreateInstance(); + _document._irefTable.Add(obj); +#else + T result = null; +#if !NETFX_CORE && !UWP + ConstructorInfo ctorInfo = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding, + null, new Type[] { typeof(PdfDocument) }, null); +#else + ConstructorInfo ctorInfo = null; // TODO +#endif + if (ctorInfo != null) + { + result = (T)ctorInfo.Invoke(new object[] { _document }); + Debug.Assert(result != null); + AddObject(result); + } + Debug.Assert(result != null, "CreateIndirectObject failed with type " + typeof(T).FullName); +#endif + return obj; + } + + /// + /// Adds an object to the PDF document. This operation and only this operation makes the object + /// an indirect object owned by this document. + /// + public void AddObject(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (obj.Owner == null) + obj.Document = _document; + else if (obj.Owner != _document) + throw new InvalidOperationException("Object does not belong to this document."); + _document._irefTable.Add(obj); + } + + /// + /// Removes an object from the PDF document. + /// + public void RemoveObject(PdfObject obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + if (obj.Reference == null) + throw new InvalidOperationException("Only indirect objects can be removed."); + if (obj.Owner != _document) + throw new InvalidOperationException("Object does not belong to this document."); + + _document._irefTable.Remove(obj.Reference); + } + + /// + /// Returns an array containing the specified object as first element follows by its transitive + /// closure. The closure of an object are all objects that can be reached by indirect references. + /// The transitive closure is the result of applying the calculation of the closure to a closure + /// as long as no new objects came along. This is e.g. useful for getting all objects belonging + /// to the resources of a page. + /// + public PdfObject[] GetClosure(PdfObject obj) + { + return GetClosure(obj, Int32.MaxValue); + } + + /// + /// Returns an array containing the specified object as first element follows by its transitive + /// closure limited by the specified number of iterations. + /// + public PdfObject[] GetClosure(PdfObject obj, int depth) + { + PdfReference[] references = _document._irefTable.TransitiveClosure(obj, depth); + int count = references.Length + 1; + PdfObject[] objects = new PdfObject[count]; + objects[0] = obj; + for (int idx = 1; idx < count; idx++) + objects[idx] = references[idx - 1].Value; + return objects; + } + + /// + /// Writes a PdfItem into the specified stream. + /// + // This function exists to keep PdfWriter and PdfItem.WriteObject internal. + public void WriteObject(Stream stream, PdfItem item) + { + // Never write an encrypted object + PdfWriter writer = new PdfWriter(stream, null); + writer.Options = PdfWriterOptions.OmitStream; + item.WriteObject(writer); + } + + /// + /// The name of the custom value key. + /// + public string CustomValueKey = "/PdfSharp.CustomValue"; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNameDictionary.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNameDictionary.cs new file mode 100644 index 00000000..cc1d25f6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNameDictionary.cs @@ -0,0 +1,151 @@ +using System.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents the name dictionary. + /// + public sealed class PdfNameDictionary : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfNameDictionary(PdfDocument document) + : base(document) + { + } + + internal PdfNameDictionary(PdfDictionary dictionary) + : base(dictionary) + { + } + + internal void AddNamedDestination(string destinationName, int destinationPage, PdfNamedDestinationParameters parameters) + { + if (_dests == null) + { + _dests = new PdfNameTreeNode(true); + Owner.Internals.AddObject(_dests); + Elements.SetReference(Keys.Dests, _dests.Reference); + } + + // destIndex > Owner.PageCount can happen when rendering pages using PDFsharp directly. + int destIndex = destinationPage; + if (destIndex > Owner.PageCount) + destIndex = Owner.PageCount; + destIndex--; + PdfPage dest = Owner.Pages[destIndex]; + +#if true + PdfArray destination = new PdfArray(Owner, + new PdfLiteral("{0} 0 R {1}", dest.ObjectNumber, parameters)); + _dests.AddName(destinationName, destination); +#else +// Insert reference to destination dictionary instead of inserting the destination array directly. + PdfArray destination = new PdfArray(Owner, new PdfLiteral("{0} 0 R {1}", dest.ObjectNumber, parameters)); + PdfDictionary destinationDict = new PdfDictionary(Owner); + destinationDict.Elements.SetObject("/D", destination); + Owner.Internals.AddObject(destinationDict); + _dests.AddName(destinationName, destinationDict.Reference); +#endif + } + private PdfNameTreeNode _dests; + + internal void AddEmbeddedFile(string name, Stream stream) + { + if (_embeddedFiles == null) + { + _embeddedFiles = new PdfNameTreeNode(true); + Owner.Internals.AddObject(_embeddedFiles); + Elements.SetReference(Keys.EmbeddedFiles, _embeddedFiles.Reference); + } + + var embeddedFileStream = new PdfEmbeddedFileStream(Owner, stream); + var fileSpecification = new PdfFileSpecification(Owner, embeddedFileStream, name); + Owner.Internals.AddObject(fileSpecification); + + _embeddedFiles.AddName(name, fileSpecification.Reference); + } + private PdfNameTreeNode _embeddedFiles; + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Optional; PDF 1.2) A name tree mapping name strings to destinations (see “Named Destinations” on page 583). + /// + [KeyInfo("1.2", KeyType.NameTree | KeyType.Optional)] + public const string Dests = "/Dests"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping name strings to annotation appearance streams + ///// (see Section 8.4.4, “Appearance Streams”). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string AP = "/AP"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping name strings to document-level JavaScript actions + ///// (see “JavaScript Actions” on page 709). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string JavaScript = "/JavaScript"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping name strings to visible pages for use in interactive forms + ///// (see Section 8.6.5, “Named Pages”). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string Pages = "/Pages"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping name strings to invisible (template) pages for use in + ///// interactive forms (see Section 8.6.5, “Named Pages”). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string Templates = "/Templates"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping digital identifiers to Web Capture content sets + ///// (see Section 10.9.3, “Content Sets”). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string IDS = "/IDS"; + + ///// + ///// (Optional; PDF 1.3) A name tree mapping uniform resource locators (URLs) to Web Capture content sets + ///// (see Section 10.9.3, “Content Sets”). + ///// + //[KeyInfo("1.3", KeyType.NameTree | KeyType.Optional)] + //public const string URLS = "/URLS"; + + /// + /// (Optional; PDF 1.4) A name tree mapping name strings to file specifications for embedded file streams + /// (see Section 3.10.3, “Embedded File Streams”). + /// + [KeyInfo("1.4", KeyType.NameTree | KeyType.Optional)] + public const string EmbeddedFiles = "/EmbeddedFiles"; + + ///// + ///// (Optional; PDF 1.4) A name tree mapping name strings to alternate presentations + ///// (see Section 9.4, “Alternate Presentations”). + ///// + //[KeyInfo("1.4", KeyType.NameTree | KeyType.Optional)] + //public const string AlternatePresentations = "/AlternatePresentations"; + + ///// + ///// (Optional; PDF 1.5) A name tree mapping name strings (which must have Unicode encoding) to + ///// rendition objects (see Section 9.1.2, “Renditions”). + ///// + //[KeyInfo("1.5", KeyType.NameTree | KeyType.Optional)] + //public const string Renditions = "/Renditions"; + + // ReSharper restore InconsistentNaming + } + + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNamedDestinationParameters.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNamedDestinationParameters.cs new file mode 100644 index 00000000..87493783 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfNamedDestinationParameters.cs @@ -0,0 +1,179 @@ +using PdfSharp.Drawing; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Creates the named destination parameters. + /// + public class PdfNamedDestinationParameters + { + private readonly string _parameters; + + private PdfNamedDestinationParameters(string parameters) + { + _parameters = parameters; + } + + private static PdfNamedDestinationParameters CreateXYZ(double? left, double? top, double? zoom) + { + return new PdfNamedDestinationParameters(Format("/XYZ {0} {1} {2}", left, top, zoom)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will only move to the destination page, without changing the left, top and zoom values for the displayed area. + /// + public static PdfNamedDestinationParameters CreateUnchangedPosition() + { + return CreateXYZ(null, null, null); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired top value and the optional zoom value on the destination page. The left value for the displayed area and null values are retained unchanged. + /// + /// The top value of the displayed area in PDF world space units. + /// Optional: The zoom value for the displayed area. 1 = 100%, 2 = 200% etc. + public static PdfNamedDestinationParameters CreateVerticalPosition(double? top, double? zoom = null) + { + return CreateXYZ(null, top, zoom); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired left and top value and the optional zoom value on the destination page. Null values are retained unchanged. + /// + /// The left value of the displayed area in PDF world space units. + /// The top value of the displayed area in PDF world space units. + /// Optional: The zoom value for the displayed area. 1 = 100%, 2 = 200% etc. + public static PdfNamedDestinationParameters CreatePosition(double? left, double? top, double? zoom = null) + { + return CreateXYZ(left, top, zoom); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired left and top value and the optional zoom value on the destination page. Null values are retained unchanged. + /// + /// An Xpoint defining the left and top value of the displayed area in PDF world space units. + /// Optional: The zoom value for the displayed area. 1 = 100%, 2 = 200% etc. + public static PdfNamedDestinationParameters CreatePosition(XPoint position, double? zoom = null) + { + return CreateXYZ(position.X, position.Y, zoom); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the destination page, displaying the whole page. + /// + public static PdfNamedDestinationParameters CreateFit() + { + return new PdfNamedDestinationParameters("/Fit"); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired top value on the destination page. The page width ist fitted to the window. Null values are retained unchanged. + /// + /// The top value of the displayed area in PDF world space units. + public static PdfNamedDestinationParameters CreateFitHorizontally(double? top) + { + return new PdfNamedDestinationParameters(Format("/FitH {0}", top)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired left value on the destination page. The page height ist fitted to the window. Null values are retained unchanged. + /// + /// The left value of the displayed area in PDF world space units. + public static PdfNamedDestinationParameters CreateFitVertically(double? left) + { + return new PdfNamedDestinationParameters(Format("/FitV {0}", left)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the destination page. The given rectangle ist fitted to the window. + /// + /// The left value of the rectangle to display in PDF world space units. + /// The top value of the rectangle to display in PDF world space units. + /// The right value of the rectangle to display in PDF world space units. + /// The bottom value of the rectangle to display in PDF world space units. + public static PdfNamedDestinationParameters CreateFitRectangle(double left, double top, double right, double bottom) + { + return new PdfNamedDestinationParameters(Format("/FitR {0} {1} {2} {3}", left, bottom, right, top)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the destination page. The given rectangle ist fitted to the window. + /// + /// The XRect representing the rectangle to display in PDF world space units. + public static PdfNamedDestinationParameters CreateFitRectangle(XRect rect) + { + return CreateFitRectangle(rect.Left, rect.Top, rect.Right, rect.Bottom); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the destination page. The given rectangle ist fitted to the window. + /// + /// The first XPoint representing the rectangle to display in PDF world space units. + /// The second XPoint representing the rectangle to display in PDF world space units. + public static PdfNamedDestinationParameters CreateFitRectangle(XPoint point1, XPoint point2) + { + return CreateFitRectangle(new XRect(point1, point2)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the destination page. The page's bounding box is fitted to the window. + /// + public static PdfNamedDestinationParameters CreateFitBoundingBox() + { + return new PdfNamedDestinationParameters("/FitB"); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired top value on the destination page. The page's bounding box width ist fitted to the window. Null values are retained unchanged. + /// + /// The top value of the displayed area in PDF world space units. + public static PdfNamedDestinationParameters CreateFitBoundingBoxHorizontally(double? top) + { + return new PdfNamedDestinationParameters(Format("/FitBH {0}", top)); + } + + /// + /// Creates a PdfNamedDestinationParameters object for a named destination. + /// Moving to this destination will move to the desired left value on the destination page. The page's bounding box height ist fitted to the window. Null values are retained unchanged. + /// + /// The left value of the displayed area in PDF world space units. + public static PdfNamedDestinationParameters CreateFitBoundingBoxVertically(double? left) + { + return new PdfNamedDestinationParameters(Format("/FitBV {0}", left)); + } + + private static string Format(string format, params double?[] values) + { + int length = values.Length; + object[] objValues = new object[length]; + for (int i = 0; i < length; i++) + { + objValues[i] = values[i] ?? (object)"null"; + } + + return PdfEncoders.Format(format, objValues); + } + + /// + /// Returns the parameters string for the named destination. + /// + /// + public override string ToString() + { + return _parameters; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectInternals.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectInternals.cs new file mode 100644 index 00000000..93640aa0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectInternals.cs @@ -0,0 +1,84 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Provides access to the internal PDF object data structures. This class prevents the public + /// interfaces from pollution with to much internal functions. + /// + public class PdfObjectInternals + { + internal PdfObjectInternals(PdfObject obj) + { + _obj = obj; + } + readonly PdfObject _obj; + + /// + /// Gets the object identifier. Returns PdfObjectID.Empty for direct objects. + /// + public PdfObjectID ObjectID + { + get { return _obj.ObjectID; } + } + + /// + /// Gets the object number. + /// + public int ObjectNumber + { + get { return _obj.ObjectID.ObjectNumber; } + } + + /// + /// Gets the generation number. + /// + public int GenerationNumber + { + get { return _obj.ObjectID.GenerationNumber; } + } + + /// + /// Gets the name of the current type. + /// Not a very useful property, but can be used for data binding. + /// + public string TypeID + { + get + { + if (_obj is PdfArray) + return "array"; + if (_obj is PdfDictionary) + return "dictionary"; + return _obj.GetType().Name; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectStream.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectStream.cs new file mode 100644 index 00000000..2517957c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfObjectStream.cs @@ -0,0 +1,173 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an object stream that contains compressed objects. + /// PDF 1.5. + /// + public class PdfObjectStream : PdfDictionary + { + // Reference: 3.4.6 Object Streams / Page 100 + + /// + /// Initializes a new instance of the class. + /// + public PdfObjectStream(PdfDocument document) + : base(document) + { +#if DEBUG && CORE + if (Internal.PdfDiagnostics.TraceObjectStreams) + { + Debug.WriteLine("PdfObjectStream(document) created."); + } +#endif + } + + /// + /// Initializes a new instance from an existing dictionary. Used for object type transformation. + /// + internal PdfObjectStream(PdfDictionary dict) + : base(dict) + { + int n = Elements.GetInteger(Keys.N); + int first = Elements.GetInteger(Keys.First); + Stream.TryUnfilter(); + + Parser parser = new Parser(null, new MemoryStream(Stream.Value)); + _header = parser.ReadObjectStreamHeader(n, first); + +#if DEBUG && CORE + if (Internal.PdfDiagnostics.TraceObjectStreams) + { + Debug.WriteLine(String.Format("PdfObjectStream(document) created. Header item count: {0}", _header.GetLength(0))); + } +#endif + } + + /// + /// Reads the compressed object with the specified index. + /// + internal void ReadReferences(PdfCrossReferenceTable xrefTable) + { + ////// Create parser for stream. + ////Parser parser = new Parser(_document, new MemoryStream(Stream.Value)); + for (int idx = 0; idx < _header.Length; idx++) + { + int objectNumber = _header[idx][0]; + int offset = _header[idx][1]; + + PdfObjectID objectID = new PdfObjectID(objectNumber); + + // HACK: -1 indicates compressed object. + PdfReference iref = new PdfReference(objectID, -1); + ////iref.ObjectID = objectID; + ////iref.Value = xrefStream; + if (!xrefTable.Contains(iref.ObjectID)) + { + xrefTable.Add(iref); + } + else + { + GetType(); + } + } + } + + /// + /// Reads the compressed object with the specified index. + /// + internal PdfReference ReadCompressedObject(int index) + { + Parser parser = new Parser(_document, new MemoryStream(Stream.Value)); + int objectNumber = _header[index][0]; + int offset = _header[index][1]; + return parser.ReadCompressedObject(objectNumber, offset); + } + + /// + /// N pairs of integers. + /// The first integer represents the object number of the compressed object. + /// The second integer represents the absolute offset of that object in the decoded stream, + /// i.e. the byte offset plus First entry. + /// + private readonly int[][] _header; // Reference: Page 102 + + /// + /// Predefined keys common to all font dictionaries. + /// + public class Keys : PdfStream.Keys + { + // Reference: TABLE 3.14 Additional entries specific to an object stream dictionary / Page 101 + + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be ObjStmfor an object stream. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "ObjStm")] + public const string Type = "/Type"; + + /// + /// (Required) The number of compressed objects in the stream. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string N = "/N"; + + /// + /// (Required) The byte offset (in the decoded stream) of the first + /// compressed object. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string First = "/First"; + + /// + /// (Optional) A reference to an object stream, of which the current object + /// stream is considered an extension. Both streams are considered part of + /// a collection of object streams (see below). A given collection consists + /// of a set of streams whose Extendslinks form a directed acyclic graph. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string Extends = "/Extends"; + } + } + +#if DEBUG && CORE + static class ObjectStreamDiagnostics + { + public static void AddObjectStreamXRef() + { } + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInheritableObjects.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInheritableObjects.cs new file mode 100644 index 00000000..5654d03f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInheritableObjects.cs @@ -0,0 +1,73 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF page object. + /// + internal class PdfPageInheritableObjects : PdfDictionary + { + public PdfPageInheritableObjects() + { } + + // TODO Inheritable Resources not yet supported + + /// + /// + /// + public PdfRectangle MediaBox + { + get { return _mediaBox; } + set { _mediaBox = value; } + } + PdfRectangle _mediaBox; + + public PdfRectangle CropBox + { + get { return _cropBox; } + set { _cropBox = value; } + } + PdfRectangle _cropBox; + + public int Rotate + { + get { return _rotate; } + set + { + if (value % 90 != 0) + throw new ArgumentException("The value must be a multiple of 90.", nameof(value)); + _rotate = value; + } + } + int _rotate; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInterals.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInterals.cs new file mode 100644 index 00000000..375e7a17 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfPageInterals.cs @@ -0,0 +1,41 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Advanced +{ +#if true_ // Not yet used. + /// + /// TODO + /// + public static class PdfPageInterals + { + // TODO + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfReference.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfReference.cs new file mode 100644 index 00000000..95287bc5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfReference.cs @@ -0,0 +1,243 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// With this define each iref object gets a unique number (uid) to make them distinguishable in the debugger +#define UNIQUE_IREF_ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents an indirect reference to a PdfObject. + /// + [DebuggerDisplay("iref({ObjectNumber}, {GenerationNumber})")] + public sealed class PdfReference : PdfItem + { + // About PdfReference + // + // * A PdfReference holds either the ObjectID or the PdfObject or both. + // + // * Each PdfObject has a PdfReference if and only if it is an indirect object. Direct objects have + // no PdfReference, because they are embedded in a parent objects. + // + // * PdfReference objects are used to reference PdfObject instances. A value in a PDF dictionary + // or array that is a PdfReference represents an indirect reference. A value in a PDF dictionary or + // or array that is a PdfObject represents a direct (or embeddded) object. + // + // * When a PDF file is imported, the PdfXRefTable is filled with PdfReference objects keeping the + // ObjectsIDs and file positions (offsets) of all indirect objects. + // + // * Indirect objects can easily be renumbered because they do not rely on their ObjectsIDs. + // + // * During modification of a document the ObjectID of an indirect object has no meaning, + // except that they must be different in pairs. + + /// + /// Initializes a new PdfReference instance for the specified indirect object. + /// + public PdfReference(PdfObject pdfObject) + { + if (pdfObject.Reference != null) + throw new InvalidOperationException("Must not create iref for an object that already has one."); + _value = pdfObject; + pdfObject.Reference = this; +#if UNIQUE_IREF && DEBUG + _uid = ++s_counter; +#endif + } + + /// + /// Initializes a new PdfReference instance from the specified object identifier and file position. + /// + public PdfReference(PdfObjectID objectID, int position) + { + _objectID = objectID; + _position = position; +#if UNIQUE_IREF && DEBUG + _uid = ++s_counter; +#endif + } + + /// + /// Writes the object in PDF iref table format. + /// + internal void WriteXRefEnty(PdfWriter writer) + { + // PDFsharp does not yet support PDF 1.5 object streams. + + // Each line must be exactly 20 bytes long, otherwise Acrobat repairs the file. + string text = String.Format("{0:0000000000} {1:00000} n\n", + _position, _objectID.GenerationNumber); // InUse ? 'n' : 'f'); + writer.WriteRaw(text); + } + + /// + /// Writes an indirect reference. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + + /// + /// Gets or sets the object identifier. + /// + public PdfObjectID ObjectID + { + get { return _objectID; } + set + { + // Ignore redundant invokations. + if (_objectID == value) + return; + + _objectID = value; + if (Document != null) + { + //PdfXRefTable table = Document.xrefTable; + //table.Remove(this); + //objectID = value; + //table.Add(this); + } + } + } + PdfObjectID _objectID; + + /// + /// Gets the object number of the object identifier. + /// + public int ObjectNumber + { + get { return _objectID.ObjectNumber; } + } + + /// + /// Gets the generation number of the object identifier. + /// + public int GenerationNumber + { + get { return _objectID.GenerationNumber; } + } + + /// + /// Gets or sets the file position of the related PdfObject. + /// + public int Position + { + get { return _position; } + set { _position = value; } + } + int _position; // I know it should be long, but I have never seen a 2GB PDF file. + + //public bool InUse + //{ + // get {return inUse;} + // set {inUse = value;} + //} + //bool inUse; + + /// + /// Gets or sets the referenced PdfObject. + /// + public PdfObject Value + { + get { return _value; } + set + { + Debug.Assert(value != null, "The value of a PdfReference must never be null."); + Debug.Assert(value.Reference == null || ReferenceEquals(value.Reference, this), "The reference of the value must be null or this."); + _value = value; + // value must never be null + value.Reference = this; + } + } + PdfObject _value; + + /// + /// Hack for dead objects. + /// + internal void SetObject(PdfObject value) + { + _value = value; + } + + /// + /// Gets or sets the document this object belongs to. + /// + public PdfDocument Document + { + get { return _document; } + set { _document = value; } + } + PdfDocument _document; + + /// + /// Gets a string representing the object identifier. + /// + public override string ToString() + { + return _objectID + " R"; + } + + internal static PdfReferenceComparer Comparer + { + get { return new PdfReferenceComparer(); } + } + + /// + /// Implements a comparer that compares PdfReference objects by their PdfObjectID. + /// + internal class PdfReferenceComparer : IComparer + { + public int Compare(PdfReference x, PdfReference y) + { + PdfReference l = x; + PdfReference r = y; + if (l != null) + { + if (r != null) + return l._objectID.CompareTo(r._objectID); + return -1; + } + if (r != null) + return 1; + return 0; + } + } + +#if UNIQUE_IREF && DEBUG + static int s_counter = 0; + int _uid; +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceMap.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceMap.cs new file mode 100644 index 00000000..154b488e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceMap.cs @@ -0,0 +1,72 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Base class for all dictionaries that map resource names to objects. + /// + internal class PdfResourceMap : PdfDictionary //, IEnumerable + { + public PdfResourceMap() + { } + + public PdfResourceMap(PdfDocument document) + : base(document) + { } + + protected PdfResourceMap(PdfDictionary dict) + : base(dict) + { } + + // public int Count + // { + // get {return resources.Count;} + // } + // + // public PdfObject this[string key] + // { + // get {return resources[key] as PdfObject;} + // set {resources[key] = value;} + // } + + /// + /// Adds all imported resource names to the specified hashtable. + /// + internal void CollectResourceNames(Dictionary usedResourceNames) + { + // ?TODO: Imported resources (e.g. fonts) can be reused, but I think this is rather difficult. Will be an issue in PDFsharp 2.0. + PdfName[] names = Elements.KeyNames; + foreach (PdfName name in names) + usedResourceNames.Add(name.ToString(), null); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceTable.cs new file mode 100644 index 00000000..40ce26e0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResourceTable.cs @@ -0,0 +1,58 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Base class for FontTable, ImageTable, FormXObjectTable etc. + /// + public class PdfResourceTable + { + /// + /// Base class for document wide resource tables. + /// + public PdfResourceTable(PdfDocument owner) + { + if (owner == null) + throw new ArgumentNullException("owner"); + _owner = owner; + } + + /// + /// Gets the owning document of this resource table. + /// + protected PdfDocument Owner + { + get { return _owner; } + } + readonly PdfDocument _owner; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResources.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResources.cs new file mode 100644 index 00000000..dd80e591 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfResources.cs @@ -0,0 +1,452 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF resource object. + /// + public sealed class PdfResources : PdfDictionary + { + // Resource management works roughly like this: + // When the user creates an XFont and uses it in the XGraphics of a PdfPage, then at the first time + // a PdfFont is created and cached in the document global font table. If the user creates a new + // XFont object for an exisisting PdfFont, the PdfFont object is reused. When the PdfFont is added + // to the resources of a PdfPage for the first time, it is added to the page local PdfResourceMap for + // fonts and automatically associated with a local resource name. + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfResources(PdfDocument document) + : base(document) + { + Elements[Keys.ProcSet] = new PdfLiteral("[/PDF/Text/ImageB/ImageC/ImageI]"); + } + + internal PdfResources(PdfDictionary dict) + : base(dict) + { } + + /// + /// Adds the specified font to this resource dictionary and returns its local resource name. + /// + public string AddFont(PdfFont font) + { + string name; + if (!_resources.TryGetValue(font, out name)) + { + name = NextFontName; + _resources[font] = name; + if (font.Reference == null) + Owner._irefTable.Add(font); + Fonts.Elements[name] = font.Reference; + } + return name; + } + + /// + /// Adds the specified image to this resource dictionary + /// and returns its local resource name. + /// + public string AddImage(PdfImage image) + { + string name; + if (!_resources.TryGetValue(image, out name)) + { + name = NextImageName; + _resources[image] = name; + if (image.Reference == null) + Owner._irefTable.Add(image); + XObjects.Elements[name] = image.Reference; + } + return name; + } + + /// + /// Adds the specified form object to this resource dictionary + /// and returns its local resource name. + /// + public string AddForm(PdfFormXObject form) + { + string name; + if (!_resources.TryGetValue(form, out name)) + { + name = NextFormName; + _resources[form] = name; + if (form.Reference == null) + Owner._irefTable.Add(form); + XObjects.Elements[name] = form.Reference; + } + return name; + } + + /// + /// Adds the specified graphics state to this resource dictionary + /// and returns its local resource name. + /// + public string AddExtGState(PdfExtGState extGState) + { + string name; + if (!_resources.TryGetValue(extGState, out name)) + { + name = NextExtGStateName; + _resources[extGState] = name; + if (extGState.Reference == null) + Owner._irefTable.Add(extGState); + ExtGStates.Elements[name] = extGState.Reference; + } + return name; + } + + /// + /// Adds the specified pattern to this resource dictionary + /// and returns its local resource name. + /// + public string AddPattern(PdfShadingPattern pattern) + { + string name; + if (!_resources.TryGetValue(pattern, out name)) + { + name = NextPatternName; + _resources[pattern] = name; + if (pattern.Reference == null) + Owner._irefTable.Add(pattern); + Patterns.Elements[name] = pattern.Reference; + } + return name; + } + + /// + /// Adds the specified pattern to this resource dictionary + /// and returns its local resource name. + /// + public string AddPattern(PdfTilingPattern pattern) + { + string name; + if (!_resources.TryGetValue(pattern, out name)) + { + name = NextPatternName; + _resources[pattern] = name; + if (pattern.Reference == null) + Owner._irefTable.Add(pattern); + Patterns.Elements[name] = pattern.Reference; + } + return name; + } + + /// + /// Adds the specified shading to this resource dictionary + /// and returns its local resource name. + /// + public string AddShading(PdfShading shading) + { + string name; + if (!_resources.TryGetValue(shading, out name)) + { + name = NextShadingName; + _resources[shading] = name; + if (shading.Reference == null) + Owner._irefTable.Add(shading); + Shadings.Elements[name] = shading.Reference; + } + return name; + } + + /// + /// Gets the fonts map. + /// + internal PdfResourceMap Fonts + { + get { return _fonts ?? (_fonts = (PdfResourceMap)Elements.GetValue(Keys.Font, VCF.Create)); } + } + PdfResourceMap _fonts; + + /// + /// Gets the external objects map. + /// + internal PdfResourceMap XObjects + { + get { return _xObjects ?? (_xObjects = (PdfResourceMap)Elements.GetValue(Keys.XObject, VCF.Create)); } + } + PdfResourceMap _xObjects; + + // TODO: make own class + internal PdfResourceMap ExtGStates + { + get + { + return _extGStates ?? (_extGStates = (PdfResourceMap)Elements.GetValue(Keys.ExtGState, VCF.Create)); + } + } + PdfResourceMap _extGStates; + + // TODO: make own class + internal PdfResourceMap ColorSpaces + { + get { return _colorSpaces ?? (_colorSpaces = (PdfResourceMap)Elements.GetValue(Keys.ColorSpace, VCF.Create)); } + } + PdfResourceMap _colorSpaces; + + // TODO: make own class + internal PdfResourceMap Patterns + { + get { return _patterns ?? (_patterns = (PdfResourceMap) Elements.GetValue(Keys.Pattern, VCF.Create)); } + } + PdfResourceMap _patterns; + + // TODO: make own class + internal PdfResourceMap Shadings + { + get { return _shadings ?? (_shadings = (PdfResourceMap) Elements.GetValue(Keys.Shading, VCF.Create)); } + } + PdfResourceMap _shadings; + + // TODO: make own class + internal PdfResourceMap Properties + { + get {return _properties ?? (_properties = (PdfResourceMap) Elements.GetValue(Keys.Properties, VCF.Create));} + } + PdfResourceMap _properties; + + /// + /// Gets a new local name for this resource. + /// + string NextFontName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/F{0}", _fontNumber++))) { } + return name; + } + } + int _fontNumber; + + /// + /// Gets a new local name for this resource. + /// + string NextImageName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/I{0}", _imageNumber++))) { } + return name; + } + } + int _imageNumber; + + /// + /// Gets a new local name for this resource. + /// + string NextFormName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/Fm{0}", _formNumber++))) { } + return name; + } + } + int _formNumber; + + /// + /// Gets a new local name for this resource. + /// + string NextExtGStateName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/GS{0}", _extGStateNumber++))) { } + return name; + } + } + int _extGStateNumber; + + /// + /// Gets a new local name for this resource. + /// + string NextPatternName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/Pa{0}", _patternNumber++))) ; + return name; + } + } + int _patternNumber; + + /// + /// Gets a new local name for this resource. + /// + string NextShadingName + { + get + { + string name; + while (ExistsResourceNames(name = string.Format("/Sh{0}", _shadingNumber++))) ; + return name; + } + } + int _shadingNumber; + + /// + /// Check whether a resource name is already used in the context of this resource dictionary. + /// PDF4NET uses GUIDs as resource names, but I think this weapon is to heavy. + /// + internal bool ExistsResourceNames(string name) + { + // TODO: more precise: is this page imported and is PageOptions != Replace + // BUG: + //if (!Owner.IsImported) + // return false; + + // Collect all resouce names of all imported resources. + if (_importedResourceNames == null) + { + _importedResourceNames = new Dictionary(); + + if (Elements[Keys.Font] != null) + Fonts.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.XObject] != null) + XObjects.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.ExtGState] != null) + ExtGStates.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.ColorSpace] != null) + ColorSpaces.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.Pattern] != null) + Patterns.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.Shading] != null) + Shadings.CollectResourceNames(_importedResourceNames); + + if (Elements[Keys.Properties] != null) + Properties.CollectResourceNames(_importedResourceNames); + } + return _importedResourceNames.ContainsKey(name); + // This is superfluous because PDFsharp resource names cannot be double. + // importedResourceNames.Add(name, null); + } + + /// + /// All the names of imported resources. + /// + Dictionary _importedResourceNames; + + /// + /// Maps all PDFsharp resources to their local resource names. + /// + readonly Dictionary _resources = new Dictionary(); + + /// + /// Predefined keys of this dictionary. + /// + public sealed class Keys : KeysBase + { + /// + /// (Optional) A dictionary that maps resource names to graphics state + /// parameter dictionaries. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string ExtGState = "/ExtGState"; + + /// + /// (Optional) A dictionary that maps each resource name to either the name of a + /// device-dependent color space or an array describing a color space. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string ColorSpace = "/ColorSpace"; + + /// + /// (Optional) A dictionary that maps each resource name to either the name of a + /// device-dependent color space or an array describing a color space. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string Pattern = "/Pattern"; + + /// + /// (Optional; PDF 1.3) A dictionary that maps resource names to shading dictionaries. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string Shading = "/Shading"; + + /// + /// (Optional) A dictionary that maps resource names to external objects. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string XObject = "/XObject"; + + /// + /// (Optional) A dictionary that maps resource names to font dictionaries. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string Font = "/Font"; + + /// + /// (Optional) An array of predefined procedure set names. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string ProcSet = "/ProcSet"; + + /// + /// (Optional; PDF 1.2) A dictionary that maps resource names to property list + /// dictionaries for marked content. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfResourceMap))] + public const string Properties = "/Properties"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShading.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShading.cs new file mode 100644 index 00000000..2b7ca0b8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShading.cs @@ -0,0 +1,258 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; +using PdfSharp.Drawing.Pdf; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a shading dictionary. + /// + public sealed class PdfShading : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfShading(PdfDocument document) + : base(document) + { } + + /// + /// Setups the shading from the specified brush. + /// + internal void SetupFromBrush(XLinearGradientBrush brush, XGraphicsPdfRenderer renderer) + { + if (brush == null) + throw new ArgumentNullException("brush"); + + PdfColorMode colorMode = _document.Options.ColorMode; + XColor color1 = ColorSpaceHelper.EnsureColorMode(colorMode, brush._color1); + XColor color2 = ColorSpaceHelper.EnsureColorMode(colorMode, brush._color2); + + PdfDictionary function = new PdfDictionary(); + + Elements[Keys.ShadingType] = new PdfInteger(2); + if (colorMode != PdfColorMode.Cmyk) + Elements[Keys.ColorSpace] = new PdfName("/DeviceRGB"); + else + Elements[Keys.ColorSpace] = new PdfName("/DeviceCMYK"); + + double x1 = 0, y1 = 0, x2 = 0, y2 = 0; + if (brush._useRect) + { + XPoint pt1 = renderer.WorldToView(brush._rect.TopLeft); + XPoint pt2 = renderer.WorldToView(brush._rect.BottomRight); + + switch (brush._linearGradientMode) + { + case XLinearGradientMode.Horizontal: + x1 = pt1.X; + y1 = pt1.Y; + x2 = pt2.X; + y2 = pt1.Y; + break; + + case XLinearGradientMode.Vertical: + x1 = pt1.X; + y1 = pt1.Y; + x2 = pt1.X; + y2 = pt2.Y; + break; + + case XLinearGradientMode.ForwardDiagonal: + x1 = pt1.X; + y1 = pt1.Y; + x2 = pt2.X; + y2 = pt2.Y; + break; + + case XLinearGradientMode.BackwardDiagonal: + x1 = pt2.X; + y1 = pt1.Y; + x2 = pt1.X; + y2 = pt2.Y; + break; + } + } + else + { + XPoint pt1 = renderer.WorldToView(brush._point1); + XPoint pt2 = renderer.WorldToView(brush._point2); + + x1 = pt1.X; + y1 = pt1.Y; + x2 = pt2.X; + y2 = pt2.Y; + } + + const string format = Config.SignificantFigures3; + Elements[Keys.Coords] = new PdfLiteral("[{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "}]", x1, y1, x2, y2); + + //Elements[Keys.Background] = new PdfRawItem("[0 1 1]"); + //Elements[Keys.Domain] = + Elements[Keys.Function] = function; + //Elements[Keys.Extend] = new PdfRawItem("[true true]"); + + string clr1 = "[" + PdfEncoders.ToString(color1, colorMode) + "]"; + string clr2 = "[" + PdfEncoders.ToString(color2, colorMode) + "]"; + + function.Elements["/FunctionType"] = new PdfInteger(2); + function.Elements["/C0"] = new PdfLiteral(clr1); + function.Elements["/C1"] = new PdfLiteral(clr2); + function.Elements["/Domain"] = new PdfLiteral("[0 1]"); + function.Elements["/N"] = new PdfInteger(1); + } + + /// + /// Common keys for all streams. + /// + internal sealed class Keys : KeysBase + { + /// + /// (Required) The shading type: + /// 1 Function-based shading + /// 2 Axial shading + /// 3 Radial shading + /// 4 Free-form Gouraud-shaded triangle mesh + /// 5 Lattice-form Gouraud-shaded triangle mesh + /// 6 Coons patch mesh + /// 7 Tensor-product patch mesh + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string ShadingType = "/ShadingType"; + + /// + /// (Required) The color space in which color values are expressed. This may be any device, + /// CIE-based, or special color space except a Pattern space. + /// + [KeyInfo(KeyType.NameOrArray | KeyType.Required)] + public const string ColorSpace = "/ColorSpace"; + + /// + /// (Optional) An array of color components appropriate to the color space, specifying + /// a single background color value. If present, this color is used, before any painting + /// operation involving the shading, to fill those portions of the area to be painted + /// that lie outside the bounds of the shading object. In the opaque imaging model, + /// the effect is as if the painting operation were performed twice: first with the + /// background color and then with the shading. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Background = "/Background"; + + /// + /// (Optional) An array of four numbers giving the left, bottom, right, and top coordinates, + /// respectively, of the shadings bounding box. The coordinates are interpreted in the + /// shadings target coordinate space. If present, this bounding box is applied as a temporary + /// clipping boundary when the shading is painted, in addition to the current clipping path + /// and any other clipping boundaries in effect at that time. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Optional)] + public const string BBox = "/BBox"; + + /// + /// (Optional) A flag indicating whether to filter the shading function to prevent aliasing + /// artifacts. The shading operators sample shading functions at a rate determined by the + /// resolution of the output device. Aliasing can occur if the function is not smooththat + /// is, if it has a high spatial frequency relative to the sampling rate. Anti-aliasing can + /// be computationally expensive and is usually unnecessary, since most shading functions + /// are smooth enough or are sampled at a high enough frequency to avoid aliasing effects. + /// Anti-aliasing may not be implemented on some output devices, in which case this flag + /// is ignored. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string AntiAlias = "/AntiAlias"; + + // ---- Type 2 ---------------------------------------------------------- + + /// + /// (Required) An array of four numbers [x0 y0 x1 y1] specifying the starting and + /// ending coordinates of the axis, expressed in the shadings target coordinate space. + /// + [KeyInfo(KeyType.Array | KeyType.Required)] + public const string Coords = "/Coords"; + + /// + /// (Optional) An array of two numbers [t0 t1] specifying the limiting values of a + /// parametric variable t. The variable is considered to vary linearly between these + /// two values as the color gradient varies between the starting and ending points of + /// the axis. The variable t becomes the input argument to the color function(s). + /// Default value: [0.0 1.0]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Domain = "/Domain"; + + /// + /// (Required) A 1-in, n-out function or an array of n 1-in, 1-out functions (where n + /// is the number of color components in the shading dictionarys color space). The + /// function(s) are called with values of the parametric variable t in the domain defined + /// by the Domain entry. Each functions domain must be a superset of that of the shading + /// dictionary. If the value returned by the function for a given color component is out + /// of range, it is adjusted to the nearest valid value. + /// + [KeyInfo(KeyType.Function | KeyType.Required)] + public const string Function = "/Function"; + + /// + /// (Optional) An array of two boolean values specifying whether to extend the shading + /// beyond the starting and ending points of the axis, respectively. + /// Default value: [false false]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Extend = "/Extend"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShadingPattern.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShadingPattern.cs new file mode 100644 index 00000000..0818aac5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfShadingPattern.cs @@ -0,0 +1,132 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; +using PdfSharp.Drawing.Pdf; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a shading pattern dictionary. + /// + public sealed class PdfShadingPattern : PdfDictionaryWithContentStream + { + /// + /// Initializes a new instance of the class. + /// + public PdfShadingPattern(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Pattern"); + Elements[Keys.PatternType] = new PdfInteger(2); + } + + /// + /// Setups the shading pattern from the specified brush. + /// + internal void SetupFromBrush(XLinearGradientBrush brush, XMatrix matrix, XGraphicsPdfRenderer renderer) + { + if (brush == null) + throw new ArgumentNullException("brush"); + + PdfShading shading = new PdfShading(_document); + shading.SetupFromBrush(brush, renderer); + Elements[Keys.Shading] = shading; + //Elements[Keys.Matrix] = new PdfLiteral("[" + PdfEncoders.ToString(matrix) + "]"); + Elements.SetMatrix(Keys.Matrix, matrix); + } + + /// + /// Common keys for all streams. + /// + internal sealed new class Keys : PdfDictionaryWithContentStream.Keys + { + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be Pattern for a pattern dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Type = "/Type"; + + /// + /// (Required) A code identifying the type of pattern that this dictionary describes; + /// must be 2 for a shading pattern. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string PatternType = "/PatternType"; + + /// + /// (Required) A shading object (see below) defining the shading patterns gradient fill. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Shading = "/Shading"; + + /// + /// (Optional) An array of six numbers specifying the pattern matrix. + /// Default value: the identity matrix [1 0 0 1 0 0]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Matrix = "/Matrix"; + + /// + /// (Optional) A graphics state parameter dictionary containing graphics state parameters + /// to be put into effect temporarily while the shading pattern is painted. Any parameters + /// that are not so specified are inherited from the graphics state that was in effect + /// at the beginning of the content stream in which the pattern is defined as a resource. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string ExtGState = "/ExtGState"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfSoftMask.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfSoftMask.cs new file mode 100644 index 00000000..f22994f2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfSoftMask.cs @@ -0,0 +1,110 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF soft mask. + /// + public class PdfSoftMask : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + /// The document that owns the object. + public PdfSoftMask(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Mask"); + } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : KeysBase + { + /// + /// (Optional) The type of PDF object that this dictionary describes; + /// if present, must be Mask for a soft-mask dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Mask")] + public const string Type = "/Type"; + + /// + /// (Required) A subtype specifying the method to be used in deriving the mask values + /// from the transparency group specified by the G entry: + /// Alpha: Use the groups computed alpha, disregarding its color. + /// Luminosity: Convert the groups computed color to a single-component luminosity value. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string S = "/S"; + + /// + /// (Required) A transparency group XObject to be used as the source of alpha + /// or color values for deriving the mask. If the subtype S is Luminosity, the + /// group attributes dictionary must contain a CS entry defining the color space + /// in which the compositing computation is to be performed. + /// + [KeyInfo(KeyType.Stream | KeyType.Required)] + public const string G = "/G"; + + /// + /// (Optional) An array of component values specifying the color to be used + /// as the backdrop against which to composite the transparency group XObject G. + /// This entry is consulted only if the subtype S is Luminosity. The array consists of + /// n numbers, where n is the number of components in the color space specified + /// by the CS entry in the group attributes dictionary. + /// Default value: the color spaces initial value, representing black. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string BC = "/BC"; + + /// + /// (Optional) A function object specifying the transfer function to be used in + /// deriving the mask values. The function accepts one input, the computed + /// group alpha or luminosity (depending on the value of the subtype S), and + /// returns one output, the resulting mask value. Both the input and output + /// must be in the range 0.0 to 1.0; if the computed output falls outside this + /// range, it is forced to the nearest valid value. The name Identity may be + /// specified in place of a function object to designate the identity function. + /// Default value: Identity. + /// + [KeyInfo(KeyType.FunctionOrName | KeyType.Optional)] + public const string TR = "/TR"; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTilingPattern.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTilingPattern.cs new file mode 100644 index 00000000..d2378a6f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTilingPattern.cs @@ -0,0 +1,177 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if GDI +using System.Drawing; +using System.Drawing.Imaging; +#endif +#if WPF +using System.Windows.Media; +#endif + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a tiling pattern dictionary. + /// + public sealed class PdfTilingPattern : PdfDictionaryWithContentStream + { + /// + /// Initializes a new instance of the class. + /// + public PdfTilingPattern(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Pattern"); + Elements[Keys.PatternType] = new PdfInteger(1); + } + + ///// + ///// Setups the shading pattern from the specified brush. + ///// + //public void SetupFromBrush(XLinearGradientBrush brush, XMatrix matrix) + //{ + // if (brush == null) + // throw new ArgumentNullException("brush"); + + // PdfShading shading = new PdfShading(document); + // shading.SetupFromBrush(brush); + // Elements[Keys.Shading] = shading; + // Elements[Keys.Matrix] = new PdfLiteral("[" + PdfEncoders.ToString(matrix) + "]"); + //} + + /// + /// Common keys for all streams. + /// + internal sealed new class Keys : PdfDictionaryWithContentStream.Keys + { + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be Pattern for a pattern dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Type = "/Type"; + + /// + /// (Required) A code identifying the type of pattern that this dictionary describes; + /// must be 1 for a tiling pattern. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string PatternType = "/PatternType"; + + /// + /// (Required) A code that determines how the color of the pattern cell is to be specified: + /// 1: Colored tiling pattern. The patterns content stream specifies the colors used to + /// paint the pattern cell. When the content stream begins execution, the current color + /// is the one that was initially in effect in the patterns parent content stream. + /// 2: Uncolored tiling pattern. The patterns content stream does not specify any color + /// information. Instead, the entire pattern cell is painted with a separately specified color + /// each time the pattern is used. Essentially, the content stream describes a stencil + /// through which the current color is to be poured. The content stream must not invoke + /// operators that specify colors or other color-related parameters in the graphics state; + /// otherwise, an error occurs. The content stream may paint an image mask, however, + /// since it does not specify any color information. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string PaintType = "/PaintType"; + + /// + /// (Required) A code that controls adjustments to the spacing of tiles relative to the device + /// pixel grid: + /// 1: Constant spacing. Pattern cells are spaced consistentlythat is, by a multiple of a + /// device pixel. To achieve this, the application may need to distort the pattern cell slightly + /// by making small adjustments to XStep, YStep, and the transformation matrix. The amount + /// of distortion does not exceed 1 device pixel. + /// 2: No distortion. The pattern cell is not distorted, but the spacing between pattern cells + /// may vary by as much as 1 device pixel, both horizontally and vertically, when the pattern + /// is painted. This achieves the spacing requested by XStep and YStep on average but not + /// necessarily for each individual pattern cell. + /// 3: Constant spacing and faster tiling. Pattern cells are spaced consistently as in tiling + /// type 1 but with additional distortion permitted to enable a more efficient implementation. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string TilingType = "/TilingType"; + + /// + /// (Required) An array of four numbers in the pattern coordinate system giving the + /// coordinates of the left, bottom, right, and top edges, respectively, of the pattern + /// cells bounding box. These boundaries are used to clip the pattern cell. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Optional)] + public const string BBox = "/BBox"; + + /// + /// (Required) The desired horizontal spacing between pattern cells, measured in the + /// pattern coordinate system. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string XStep = "/XStep"; + + /// + /// (Required) The desired vertical spacing between pattern cells, measured in the pattern + /// coordinate system. Note that XStep and YStep may differ from the dimensions of the + /// pattern cell implied by the BBox entry. This allows tiling with irregularly shaped figures. + /// XStep and YStep may be either positive or negative but not zero. + /// + [KeyInfo(KeyType.Real | KeyType.Required)] + public const string YStep = "/YStep"; + + /// + /// (Required) A resource dictionary containing all of the named resources required by + /// the patterns content stream (see Section 3.7.2, Resource Dictionaries). + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public new const string Resources = "/Resources"; + + /// + /// (Optional) An array of six numbers specifying the pattern matrix. + /// Default value: the identity matrix [1 0 0 1 0 0]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Matrix = "/Matrix"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfToUnicodeMap.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfToUnicodeMap.cs new file mode 100644 index 00000000..db3eea13 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfToUnicodeMap.cs @@ -0,0 +1,148 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using PdfSharp.Fonts; +using PdfSharp.Pdf.Filters; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a ToUnicode map for composite font. + /// + internal sealed class PdfToUnicodeMap : PdfDictionary + { + public PdfToUnicodeMap(PdfDocument document) + : base(document) + { } + + public PdfToUnicodeMap(PdfDocument document, CMapInfo cmapInfo) + : base(document) + { + _cmapInfo = cmapInfo; + } + + /// + /// Gets or sets the CMap info. + /// + public CMapInfo CMapInfo + { + get { return _cmapInfo; } + set { _cmapInfo = value; } + } + CMapInfo _cmapInfo; + + /// + /// Creates the ToUnicode map from the CMapInfo. + /// + internal override void PrepareForSave() + { + base.PrepareForSave(); + + // This code comes literally from PDF Reference + string prefix = + "/CIDInit /ProcSet findresource begin\n" + + "12 dict begin\n" + + "begincmap\n" + + "/CIDSystemInfo << /Registry (Adobe)/Ordering (UCS)/Supplement 0>> def\n" + + "/CMapName /Adobe-Identity-UCS def /CMapType 2 def\n"; + string suffix = "endcmap CMapName currentdict /CMap defineresource pop end end"; + + Dictionary glyphIndexToCharacter = new Dictionary(); + int lowIndex = 65536, hiIndex = -1; + foreach (KeyValuePair entry in _cmapInfo.CharacterToGlyphIndex) + { + int index = (int)entry.Value; + lowIndex = Math.Min(lowIndex, index); + hiIndex = Math.Max(hiIndex, index); + //glyphIndexToCharacter.Add(index, entry.Key); + glyphIndexToCharacter[index] = entry.Key; + } + + MemoryStream ms = new MemoryStream(); +#if !SILVERLIGHT && !NETFX_CORE + StreamWriter wrt = new StreamWriter(ms, Encoding.ASCII); +#else + StreamWriter wrt = new StreamWriter(ms, Encoding.UTF8); +#endif + wrt.Write(prefix); + + wrt.WriteLine("1 begincodespacerange"); + wrt.WriteLine(String.Format("<{0:X4}><{1:X4}>", lowIndex, hiIndex)); + wrt.WriteLine("endcodespacerange"); + + // Sorting seems not necessary. The limit is 100 entries, we will see. + wrt.WriteLine(String.Format("{0} beginbfrange", glyphIndexToCharacter.Count)); + foreach (KeyValuePair entry in glyphIndexToCharacter) + wrt.WriteLine(String.Format("<{0:X4}><{0:X4}><{1:X4}>", entry.Key, (int)entry.Value)); + wrt.WriteLine("endbfrange"); + + wrt.Write(suffix); +#if !UWP + wrt.Close(); +#else + wrt.Dispose(); +#endif + + // Compress like content streams + byte[] bytes = ms.ToArray(); +#if !UWP + ms.Close(); +#else + ms.Dispose(); +#endif + if (Owner.Options.CompressContentStreams) + { + Elements.SetName("/Filter", "/FlateDecode"); + bytes = Filtering.FlateDecode.Encode(bytes, _document.Options.FlateEncodeMode); + } + //PdfStream stream = CreateStream(bytes); + else + { + Elements.Remove("/Filter"); + } + + if (Stream == null) + CreateStream(bytes); + else + { + Stream.Value = bytes; + Elements.SetInteger(PdfStream.Keys.Length, Stream.Length); + } + } + + public sealed class Keys : PdfStream.Keys + { + // No new keys. + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrailer.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrailer.cs new file mode 100644 index 00000000..cd56e94f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrailer.cs @@ -0,0 +1,297 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Security; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF trailer dictionary. Even though trailers are dictionaries they never have a cross + /// reference entry in PdfReferenceTable. + /// + internal class PdfTrailer : PdfDictionary // Reference: 3.4.4 File Trailer / Page 96 + { + /// + /// Initializes a new instance of PdfTrailer. + /// + public PdfTrailer(PdfDocument document) + : base(document) + { + _document = document; + } + + /// + /// Initializes a new instance of the class from a . + /// + public PdfTrailer(PdfCrossReferenceStream trailer) + : base(trailer._document) + { + _document = trailer._document; + + // /ID [<09F877EBF282E9408ED1882A9A21D9F2><2A4938E896006F499AC1C2EA7BFB08E4>] + // /Info 7 0 R + // /Root 1 0 R + // /Size 10 + + PdfReference iref = trailer.Elements.GetReference(Keys.Info); + if (iref != null) + Elements.SetReference(Keys.Info, iref); + + Elements.SetReference(Keys.Root, trailer.Elements.GetReference(Keys.Root)); + + Elements.SetInteger(Keys.Size, trailer.Elements.GetInteger(Keys.Size)); + + PdfArray id = trailer.Elements.GetArray(Keys.ID); + if (id != null) + Elements.SetValue(Keys.ID, id); + } + + public int Size + { + get { return Elements.GetInteger(Keys.Size); } + set { Elements.SetInteger(Keys.Size, value); } + } + + // TODO: needed when linearized... + //public int Prev + //{ + // get {return Elements.GetInteger(Keys.Prev);} + //} + + public PdfDocumentInformation Info + { + get { return (PdfDocumentInformation)Elements.GetValue(Keys.Info, VCF.CreateIndirect); } + } + + /// + /// (Required; must be an indirect reference) + /// The catalog dictionary for the PDF document contained in the file. + /// + public PdfCatalog Root + { + get { return (PdfCatalog)Elements.GetValue(PdfTrailer.Keys.Root, VCF.CreateIndirect); } + } + + /// + /// Gets the first or second document identifier. + /// + public string GetDocumentID(int index) + { + if (index < 0 || index > 1) + throw new ArgumentOutOfRangeException("index", index, "Index must be 0 or 1."); + + PdfArray array = Elements[Keys.ID] as PdfArray; + if (array == null || array.Elements.Count < 2) + return ""; + PdfItem item = array.Elements[index]; + if (item is PdfString) + return ((PdfString)item).Value; + return ""; + } + + /// + /// Sets the first or second document identifier. + /// + public void SetDocumentID(int index, string value) + { + if (index < 0 || index > 1) + throw new ArgumentOutOfRangeException("index", index, "Index must be 0 or 1."); + + PdfArray array = Elements[Keys.ID] as PdfArray; + if (array == null || array.Elements.Count < 2) + array = CreateNewDocumentIDs(); + array.Elements[index] = new PdfString(value, PdfStringFlags.HexLiteral); + } + + /// + /// Creates and sets two identical new document IDs. + /// + internal PdfArray CreateNewDocumentIDs() + { + PdfArray array = new PdfArray(_document); + byte[] docID = Guid.NewGuid().ToByteArray(); + string id = PdfEncoders.RawEncoding.GetString(docID, 0, docID.Length); + array.Elements.Add(new PdfString(id, PdfStringFlags.HexLiteral)); + array.Elements.Add(new PdfString(id, PdfStringFlags.HexLiteral)); + Elements[Keys.ID] = array; + return array; + } + + /// + /// Gets the standard security handler. + /// + public PdfStandardSecurityHandler SecurityHandler + { + get + { + if (_securityHandler == null) + _securityHandler = (PdfStandardSecurityHandler)Elements.GetValue(Keys.Encrypt, VCF.CreateIndirect); + return _securityHandler; + } + } + internal PdfStandardSecurityHandler _securityHandler; + + internal override void WriteObject(PdfWriter writer) + { + // Delete /XRefStm entry, if any. + // HACK: + _elements.Remove(Keys.XRefStm); + + // Don't encrypt myself + PdfStandardSecurityHandler securityHandler = writer.SecurityHandler; + writer.SecurityHandler = null; + base.WriteObject(writer); + writer.SecurityHandler = securityHandler; + } + + /// + /// Replace temporary irefs by their correct counterparts from the iref table. + /// + internal void Finish() + { + // /Root + PdfReference iref = _document._trailer.Elements[Keys.Root] as PdfReference; + if (iref != null && iref.Value == null) + { + iref = _document._irefTable[iref.ObjectID]; + Debug.Assert(iref.Value != null); + _document._trailer.Elements[Keys.Root] = iref; + } + + // /Info + iref = _document._trailer.Elements[PdfTrailer.Keys.Info] as PdfReference; + if (iref != null && iref.Value == null) + { + iref = _document._irefTable[iref.ObjectID]; + Debug.Assert(iref.Value != null); + _document._trailer.Elements[Keys.Info] = iref; + } + + // /Encrypt + iref = _document._trailer.Elements[Keys.Encrypt] as PdfReference; + if (iref != null) + { + iref = _document._irefTable[iref.ObjectID]; + Debug.Assert(iref.Value != null); + _document._trailer.Elements[Keys.Encrypt] = iref; + + // The encryption dictionary (security handler) was read in before the XRefTable construction + // was completed. The next lines fix that state (it took several hours to find these bugs...). + iref.Value = _document._trailer._securityHandler; + _document._trailer._securityHandler.Reference = iref; + iref.Value.Reference = iref; + } + + Elements.Remove(Keys.Prev); + + Debug.Assert(_document._irefTable.IsUnderConstruction == false); + _document._irefTable.IsUnderConstruction = false; + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase // Reference: TABLE 3.13 Entries in the file trailer dictionary / Page 97 + { + /// + /// (Required; must not be an indirect reference) The total number of entries in the files + /// cross-reference table, as defined by the combination of the original section and all + /// update sections. Equivalently, this value is 1 greater than the highest object number + /// used in the file. + /// Note: Any object in a cross-reference section whose number is greater than this value is + /// ignored and considered missing. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Size = "/Size"; + + /// + /// (Present only if the file has more than one cross-reference section; must not be an indirect + /// reference) The byte offset from the beginning of the file to the beginning of the previous + /// cross-reference section. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Prev = "/Prev"; + + /// + /// (Required; must be an indirect reference) The catalog dictionary for the PDF document + /// contained in the file. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required, typeof(PdfCatalog))] + public const string Root = "/Root"; + + /// + /// (Required if document is encrypted; PDF 1.1) The documents encryption dictionary. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfStandardSecurityHandler))] + public const string Encrypt = "/Encrypt"; + + /// + /// (Optional; must be an indirect reference) The documents information dictionary. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional, typeof(PdfDocumentInformation))] + public const string Info = "/Info"; + + /// + /// (Optional, but strongly recommended; PDF 1.1) An array of two strings constituting + /// a file identifier for the file. Although this entry is optional, + /// its absence might prevent the file from functioning in some workflows + /// that depend on files being uniquely identified. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string ID = "/ID"; + + /// + /// (Optional) The byte offset from the beginning of the file of a cross-reference stream. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string XRefStm = "/XRefStm"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTransparencyGroupAttributes.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTransparencyGroupAttributes.cs new file mode 100644 index 00000000..1aa0645f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTransparencyGroupAttributes.cs @@ -0,0 +1,129 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a PDF transparency group XObject. + /// + public sealed class PdfTransparencyGroupAttributes : PdfGroupAttributes + { + internal PdfTransparencyGroupAttributes(PdfDocument thisDocument) + : base(thisDocument) + { + Elements.SetName(Keys.S, "/Transparency"); + } + + /// + /// Predefined keys of this dictionary. + /// + public sealed new class Keys : PdfGroupAttributes.Keys + { + /// + /// (Sometimes required, as discussed below) + /// The group color space, which is used for the following purposes: + /// As the color space into which colors are converted when painted into the group + /// As the blending color space in which objects are composited within the group + /// As the color space of the group as a whole when it in turn is painted as an object onto its backdrop + /// The group color space may be any device or CIE-based color space that + /// treats its components as independent additive or subtractive values in the + /// range 0.0 to 1.0, subject to the restrictions described in Section 7.2.3, Blending Color Space. + /// These restrictions exclude Lab and lightness-chromaticity ICCBased color spaces, + /// as well as the special color spaces Pattern, Indexed, Separation, and DeviceN. + /// Device color spaces are subject to remapping according to the DefaultGray, + /// DefaultRGB, and DefaultCMYK entries in the ColorSpace subdictionary of the + /// current resource dictionary. + /// Ordinarily, the CS entry is allowed only for isolated transparency groups + /// (those for which I, below, is true), and even then it is optional. However, + /// this entry is required in the group attributes dictionary for any transparency + /// group XObject that has no parent group or page from which to inherit in + /// particular, one that is the value of the G entry in a soft-mask dictionary of + /// subtype Luminosity. + /// In addition, it is always permissible to specify CS in the group attributes + /// dictionary associated with a page object, even if I is false or absent. In the + /// normal case in which the page is imposed directly on the output medium, + /// the page group is effectively isolated regardless of the I value, and the + /// specified CS value is therefore honored. But if the page is in turn used as an + /// element of some other page and if the group is non-isolated, CS is ignored + /// and the color space is inherited from the actual backdrop with which the + /// page is composited. + /// Default value: the color space of the parent group or page into which this + /// transparency group is painted. (The parents color space in turn can be + /// either explicitly specified or inherited.) + /// + [KeyInfo(KeyType.NameOrArray | KeyType.Optional)] + public const string CS = "/CS"; + + /// + /// (Optional) A flag specifying whether the transparency group is isolated. + /// If this flag is true, objects within the group are composited against a fully + /// transparent initial backdrop; if false, they are composited against the + /// groups backdrop. + /// Default value: false. + /// In the group attributes dictionary for a page, the interpretation of this + /// entry is slightly altered. In the normal case in which the page is imposed + /// directly on the output medium, the page group is effectively isolated and + /// the specified I value is ignored. But if the page is in turn used as an + /// element of some other page, it is treated as if it were a transparency + /// group XObject; the I value is interpreted in the normal way to determine + /// whether the page group is isolated. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string I = "/I"; + + /// + /// (Optional) A flag specifying whether the transparency group is a knockout + /// group. If this flag is false, later objects within the group are composited + /// with earlier ones with which they overlap; if true, they are composited with + /// the groups initial backdrop and overwrite (knock out) any earlier + /// overlapping objects. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string K = "/K"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static new DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrueTypeFont.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrueTypeFont.cs new file mode 100644 index 00000000..69d7281e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfTrueTypeFont.cs @@ -0,0 +1,254 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Drawing; +using PdfSharp.Pdf.Filters; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a TrueType font. + /// + internal class PdfTrueTypeFont : PdfFont + { + public PdfTrueTypeFont(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of PdfTrueTypeFont from an XFont. + /// + public PdfTrueTypeFont(PdfDocument document, XFont font) + : base(document) + { + Elements.SetName(Keys.Type, "/Font"); + Elements.SetName(Keys.Subtype, "/TrueType"); + + // TrueType with WinAnsiEncoding only. + OpenTypeDescriptor ttDescriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptorFor(font); + FontDescriptor = new PdfFontDescriptor(document, ttDescriptor); + _fontOptions = font.PdfOptions; + Debug.Assert(_fontOptions != null); + + //cmapInfo = new CMapInfo(null/*ttDescriptor*/); + _cmapInfo = new CMapInfo(ttDescriptor); + + BaseFont = font.GlyphTypeface.GetBaseName(); + + if (_fontOptions.FontEmbedding == PdfFontEmbedding.Always) + BaseFont = PdfFont.CreateEmbeddedFontSubsetName(BaseFont); + FontDescriptor.FontName = BaseFont; + + Debug.Assert(_fontOptions.FontEncoding == PdfFontEncoding.WinAnsi); + if (!IsSymbolFont) + Encoding = "/WinAnsiEncoding"; + + Owner._irefTable.Add(FontDescriptor); + Elements[Keys.FontDescriptor] = FontDescriptor.Reference; + + FontEncoding = font.PdfOptions.FontEncoding; + } + + XPdfFontOptions FontOptions + { + get { return _fontOptions; } + } + readonly XPdfFontOptions _fontOptions; + + public string BaseFont + { + get { return Elements.GetName(Keys.BaseFont); } + set { Elements.SetName(Keys.BaseFont, value); } + } + + public int FirstChar + { + get { return Elements.GetInteger(Keys.FirstChar); } + set { Elements.SetInteger(Keys.FirstChar, value); } + } + + public int LastChar + { + get { return Elements.GetInteger(Keys.LastChar); } + set { Elements.SetInteger(Keys.LastChar, value); } + } + + public PdfArray Widths + { + get { return (PdfArray)Elements.GetValue(Keys.Widths, VCF.Create); } + } + + public string Encoding + { + get { return Elements.GetName(Keys.Encoding); } + set { Elements.SetName(Keys.Encoding, value); } + } + + /// + /// Prepares the object to get saved. + /// + internal override void PrepareForSave() + { + base.PrepareForSave(); + + // Fonts are always embedded. + OpenTypeFontface subSet = FontDescriptor._descriptor.FontFace.CreateFontSubSet(_cmapInfo.GlyphIndices, false); + byte[] fontData = subSet.FontSource.Bytes; + + PdfDictionary fontStream = new PdfDictionary(Owner); + Owner.Internals.AddObject(fontStream); + FontDescriptor.Elements[PdfFontDescriptor.Keys.FontFile2] = fontStream.Reference; + + fontStream.Elements["/Length1"] = new PdfInteger(fontData.Length); + if (!Owner.Options.NoCompression) + { + fontData = Filtering.FlateDecode.Encode(fontData, _document.Options.FlateEncodeMode); + fontStream.Elements["/Filter"] = new PdfName("/FlateDecode"); + } + fontStream.Elements["/Length"] = new PdfInteger(fontData.Length); + fontStream.CreateStream(fontData); + + FirstChar = 0; + LastChar = 255; + PdfArray width = Widths; + //width.Elements.Clear(); + for (int idx = 0; idx < 256; idx++) + width.Elements.Add(new PdfInteger(FontDescriptor._descriptor.Widths[idx])); + } + + /// + /// Predefined keys of this dictionary. + /// + public new sealed class Keys : PdfFont.Keys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Font for a font dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Font")] + public new const string Type = "/Type"; + + /// + /// (Required) The type of font; must be TrueType for a TrueType font. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string Subtype = "/Subtype"; + + /// + /// (Required in PDF 1.0; optional otherwise) The name by which this font is + /// referenced in the Font subdictionary of the current resource dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Name = "/Name"; + + /// + /// (Required) The PostScript name of the font. For Type 1 fonts, this is usually + /// the value of the FontName entry in the font program; for more information. + /// The Post-Script name of the font can be used to find the fonts definition in + /// the consumer application or its environment. It is also the name that is used when + /// printing to a PostScript output device. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string BaseFont = "/BaseFont"; + + /// + /// (Required except for the standard 14 fonts) The first character code defined + /// in the fonts Widths array. + /// + [KeyInfo(KeyType.Integer)] + public const string FirstChar = "/FirstChar"; + + /// + /// (Required except for the standard 14 fonts) The last character code defined + /// in the fonts Widths array. + /// + [KeyInfo(KeyType.Integer)] + public const string LastChar = "/LastChar"; + + /// + /// (Required except for the standard 14 fonts; indirect reference preferred) + /// An array of (LastChar - FirstChar + 1) widths, each element being the glyph width + /// for the character code that equals FirstChar plus the array index. For character + /// codes outside the range FirstChar to LastChar, the value of MissingWidth from the + /// FontDescriptor entry for this font is used. The glyph widths are measured in units + /// in which 1000 units corresponds to 1 unit in text space. These widths must be + /// consistent with the actual widths given in the font program. + /// + [KeyInfo(KeyType.Array, typeof(PdfArray))] + public const string Widths = "/Widths"; + + /// + /// (Required except for the standard 14 fonts; must be an indirect reference) + /// A font descriptor describing the fonts metrics other than its glyph widths. + /// Note: For the standard 14 fonts, the entries FirstChar, LastChar, Widths, and + /// FontDescriptor must either all be present or all be absent. Ordinarily, they are + /// absent; specifying them enables a standard font to be overridden. + /// + [KeyInfo(KeyType.Dictionary | KeyType.MustBeIndirect, typeof(PdfFontDescriptor))] + public new const string FontDescriptor = "/FontDescriptor"; + + /// + /// (Optional) A specification of the fonts character encoding if different from its + /// built-in encoding. The value of Encoding is either the name of a predefined + /// encoding (MacRomanEncoding, MacExpertEncoding, or WinAnsiEncoding, as described in + /// Appendix D) or an encoding dictionary that specifies differences from the fonts + /// built-in encoding or from a specified predefined encoding. + /// + [KeyInfo(KeyType.Name | KeyType.Dictionary)] + public const string Encoding = "/Encoding"; + + /// + /// (Optional; PDF 1.2) A stream containing a CMap file that maps character + /// codes to Unicode values. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string ToUnicode = "/ToUnicode"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType0Font.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType0Font.cs new file mode 100644 index 00000000..eecff231 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType0Font.cs @@ -0,0 +1,241 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Text; +using PdfSharp.Fonts; +using PdfSharp.Fonts.OpenType; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Represents a composite font. Used for Unicode encoding. + /// + internal sealed class PdfType0Font : PdfFont + { + public PdfType0Font(PdfDocument document) + : base(document) + { } + + public PdfType0Font(PdfDocument document, XFont font, bool vertical) + : base(document) + { + Elements.SetName(Keys.Type, "/Font"); + Elements.SetName(Keys.Subtype, "/Type0"); + Elements.SetName(Keys.Encoding, vertical ? "/Identity-V" : "/Identity-H"); + + OpenTypeDescriptor ttDescriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptorFor(font); + FontDescriptor = new PdfFontDescriptor(document, ttDescriptor); + _fontOptions = font.PdfOptions; + Debug.Assert(_fontOptions != null); + + _cmapInfo = new CMapInfo(ttDescriptor); + _descendantFont = new PdfCIDFont(document, FontDescriptor, font); + _descendantFont.CMapInfo = _cmapInfo; + + // Create ToUnicode map + _toUnicode = new PdfToUnicodeMap(document, _cmapInfo); + document.Internals.AddObject(_toUnicode); + Elements.Add(Keys.ToUnicode, _toUnicode); + + BaseFont = font.GlyphTypeface.GetBaseName(); + // CID fonts are always embedded + BaseFont = PdfFont.CreateEmbeddedFontSubsetName(BaseFont); + + FontDescriptor.FontName = BaseFont; + _descendantFont.BaseFont = BaseFont; + + PdfArray descendantFonts = new PdfArray(document); + Owner._irefTable.Add(_descendantFont); + descendantFonts.Elements.Add(_descendantFont.Reference); + Elements[Keys.DescendantFonts] = descendantFonts; + } + + public PdfType0Font(PdfDocument document, string idName, byte[] fontData, bool vertical) + : base(document) + { + Elements.SetName(Keys.Type, "/Font"); + Elements.SetName(Keys.Subtype, "/Type0"); + Elements.SetName(Keys.Encoding, vertical ? "/Identity-V" : "/Identity-H"); + + OpenTypeDescriptor ttDescriptor = (OpenTypeDescriptor)FontDescriptorCache.GetOrCreateDescriptor(idName, fontData); + FontDescriptor = new PdfFontDescriptor(document, ttDescriptor); + _fontOptions = new XPdfFontOptions(PdfFontEncoding.Unicode); + Debug.Assert(_fontOptions != null); + + _cmapInfo = new CMapInfo(ttDescriptor); + _descendantFont = new PdfCIDFont(document, FontDescriptor, fontData); + _descendantFont.CMapInfo = _cmapInfo; + + // Create ToUnicode map + _toUnicode = new PdfToUnicodeMap(document, _cmapInfo); + document.Internals.AddObject(_toUnicode); + Elements.Add(Keys.ToUnicode, _toUnicode); + + //BaseFont = ttDescriptor.FontName.Replace(" ", ""); + BaseFont = ttDescriptor.FontName; + + // CID fonts are always embedded + if (!BaseFont.Contains("+")) // HACK in PdfType0Font + BaseFont = CreateEmbeddedFontSubsetName(BaseFont); + + FontDescriptor.FontName = BaseFont; + _descendantFont.BaseFont = BaseFont; + + PdfArray descendantFonts = new PdfArray(document); + Owner._irefTable.Add(_descendantFont); + descendantFonts.Elements.Add(_descendantFont.Reference); + Elements[Keys.DescendantFonts] = descendantFonts; + } + + XPdfFontOptions FontOptions + { + get { return _fontOptions; } + } + XPdfFontOptions _fontOptions; + + public string BaseFont + { + get { return Elements.GetName(Keys.BaseFont); } + set { Elements.SetName(Keys.BaseFont, value); } + } + + internal PdfCIDFont DescendantFont + { + get { return _descendantFont; } + } + readonly PdfCIDFont _descendantFont; + + internal override void PrepareForSave() + { + base.PrepareForSave(); + + // Use GetGlyphIndices to create the widths array. + OpenTypeDescriptor descriptor = (OpenTypeDescriptor)FontDescriptor._descriptor; + StringBuilder w = new StringBuilder("["); + if (_cmapInfo != null) + { + int[] glyphIndices = _cmapInfo.GetGlyphIndices(); + int count = glyphIndices.Length; + int[] glyphWidths = new int[count]; + + for (int idx = 0; idx < count; idx++) + glyphWidths[idx] = descriptor.GlyphIndexToPdfWidth(glyphIndices[idx]); + + //TODO: optimize order of indices + + for (int idx = 0; idx < count; idx++) + w.AppendFormat("{0}[{1}]", glyphIndices[idx], glyphWidths[idx]); + w.Append("]"); + _descendantFont.Elements.SetValue(PdfCIDFont.Keys.W, new PdfLiteral(w.ToString())); + + } + _descendantFont.PrepareForSave(); + _toUnicode.PrepareForSave(); + } + + /// + /// Predefined keys of this dictionary. + /// + public new sealed class Keys : PdfFont.Keys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Font for a font dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Font")] + public new const string Type = "/Type"; + + /// + /// (Required) The type of font; must be Type0 for a Type 0 font. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string Subtype = "/Subtype"; + + /// + /// (Required) The PostScript name of the font. In principle, this is an arbitrary + /// name, since there is no font program associated directly with a Type 0 font + /// dictionary. The conventions described here ensure maximum compatibility + /// with existing Acrobat products. + /// If the descendant is a Type 0 CIDFont, this name should be the concatenation + /// of the CIDFonts BaseFont name, a hyphen, and the CMap name given in the + /// Encoding entry (or the CMapName entry in the CMap). If the descendant is a + /// Type 2 CIDFont, this name should be the same as the CIDFonts BaseFont name. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string BaseFont = "/BaseFont"; + + /// + /// (Required) The name of a predefined CMap, or a stream containing a CMap + /// that maps character codes to font numbers and CIDs. If the descendant is a + /// Type 2 CIDFont whose associated TrueType font program is not embedded + /// in the PDF file, the Encoding entry must be a predefined CMap name. + /// + [KeyInfo(KeyType.StreamOrName | KeyType.Required)] + public const string Encoding = "/Encoding"; + + /// + /// (Required) A one-element array specifying the CIDFont dictionary that is the + /// descendant of this Type 0 font. + /// + [KeyInfo(KeyType.Array | KeyType.Required)] + public const string DescendantFonts = "/DescendantFonts"; + + /// + /// ((Optional) A stream containing a CMap file that maps character codes to + /// Unicode values. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string ToUnicode = "/ToUnicode"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get + { + if (Keys._meta == null) + Keys._meta = CreateMeta(typeof(Keys)); + return Keys._meta; + } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType1Font.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType1Font.cs new file mode 100644 index 00000000..9e43eb11 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfType1Font.cs @@ -0,0 +1,184 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if true_ // Not yet implemented- + +using System; +using System.Collections; +using System.Text; +using System.IO; +using PdfSharp.Internal; + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Not implemented - just for illustration of the class hierarchy. + /// + internal sealed class PdfType1Font : PdfFont + { + public PdfType1Font(PdfDocument document) + : base(document) + { + Elements["\\Type"] = new PdfName("Font"); + Elements["\\Subtype"] = new PdfName("Type1"); + } + + //public string BaseFont + //{ + // get {return baseFont;} + // set {baseFont = value;} + //} + //string baseFont; + + + // internal override void AssignObjectID(ref int objectID) + // { + // SetObjectID(ref objectID); + // } + // + // internal override void WriteObject(Stream stream) + // { + // base.WriteObject(stream); + // StringBuilder pdf = new StringBuilder(); + // pdf.AppendFormat("{0} 0 obj\n<<\n/Type /Font\n/Subtype /Type1\n/BaseFont /Helvetica\n/Encoding /WinAnsiEncoding\n>>\nendobj\n", ObjectID); + // WriteString(stream, pdf.ToString()); + // } + /// + /// Predefined keys of this dictionary. + /// + public new sealed class Keys : PdfFont.Keys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Font for a font dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Font")] + public new const string Type = "/Type"; + + /// + /// (Required) The type of font; must be Type1 for a Type 1 font. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string Subtype = "/Subtype"; + + /// + /// (Required in PDF 1.0; optional otherwise) The name by which this font is + /// referenced in the Font subdictionary of the current resource dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Name = "/Name"; + + /// + /// (Required) The PostScript name of the font. For Type 1 fonts, this is usually + /// the value of the FontName entry in the font program; for more information. + /// The Post-Script name of the font can be used to find the fonts definition in + /// the consumer application or its environment. It is also the name that is used when + /// printing to a PostScript output device. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public new const string BaseFont = "/BaseFont"; + + /// + /// (Required except for the standard 14 fonts) The first character code defined + /// in the fonts Widths array. + /// + [KeyInfo(KeyType.Integer)] + public const string FirstChar = "/FirstChar"; + + /// + /// (Required except for the standard 14 fonts) The last character code defined + /// in the fonts Widths array. + /// + [KeyInfo(KeyType.Integer)] + public const string LastChar = "/LastChar"; + + /// + /// (Required except for the standard 14 fonts; indirect reference preferred) + /// An array of (LastChar - FirstChar + 1) widths, each element being the glyph width + /// for the character code that equals FirstChar plus the array index. For character + /// codes outside the range FirstChar to LastChar, the value of MissingWidth from the + /// FontDescriptor entry for this font is used. The glyph widths are measured in units + /// in which 1000 units corresponds to 1 unit in text space. These widths must be + /// consistent with the actual widths given in the font program. + /// + [KeyInfo(KeyType.Array, typeof(PdfArray))] + public const string Widths = "/Widths"; + + /// + /// (Required except for the standard 14 fonts; must be an indirect reference) + /// A font descriptor describing the fonts metrics other than its glyph widths. + /// Note: For the standard 14 fonts, the entries FirstChar, LastChar, Widths, and + /// FontDescriptor must either all be present or all be absent. Ordinarily, they are + /// absent; specifying them enables a standard font to be overridden. + /// + [KeyInfo(KeyType.Dictionary | KeyType.MustBeIndirect, typeof(PdfFontDescriptor))] + public new const string FontDescriptor = "/FontDescriptor"; + + /// + /// (Optional) A specification of the fonts character encoding if different from its + /// built-in encoding. The value of Encoding is either the name of a predefined + /// encoding (MacRomanEncoding, MacExpertEncoding, or WinAnsiEncoding, as described in + /// Appendix D) or an encoding dictionary that specifies differences from the fonts + /// built-in encoding or from a specified predefined encoding. + /// + [KeyInfo(KeyType.Name | KeyType.Dictionary)] + public const string Encoding = "/Encoding"; + + /// + /// (Optional; PDF 1.2) A stream containing a CMap file that maps character + /// codes to Unicode values. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string ToUnicode = "/ToUnicode"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get + { + if (_meta == null) + _meta = CreateMeta(typeof(Keys)); + return _meta; + } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfXObject.cs b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfXObject.cs new file mode 100644 index 00000000..6d30bd02 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Advanced/PdfXObject.cs @@ -0,0 +1,51 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Advanced +{ + /// + /// Base class for all PDF external objects. + /// + public abstract class PdfXObject : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + /// The document that owns the object. + protected PdfXObject(PdfDocument document) + : base(document) + { } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : PdfStream.Keys + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotation.cs new file mode 100644 index 00000000..077fc6b9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotation.cs @@ -0,0 +1,399 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents the base class of all annotations. + /// + public abstract class PdfAnnotation : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + protected PdfAnnotation() + { + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + protected PdfAnnotation(PdfDocument document) + : base(document) + { + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + internal PdfAnnotation(PdfDictionary dict) + : base(dict) + { } + + void Initialize() + { + Elements.SetName(Keys.Type, "/Annot"); + Elements.SetString(Keys.NM, Guid.NewGuid().ToString("D")); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + + /// + /// Removes an annotation from the document + /// + /// + [Obsolete("Use 'Parent.Remove(this)'")] + public void Delete() + { + Parent.Remove(this); + } + + /// + /// Gets or sets the annotation flags of this instance. + /// + public PdfAnnotationFlags Flags + { + get { return (PdfAnnotationFlags)Elements.GetInteger(Keys.F); } + set + { + Elements.SetInteger(Keys.F, (int)value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets the PdfAnnotations object that this annotation belongs to. + /// + public PdfAnnotations Parent + { + get { return _parent; } + set { _parent = value; } + } + PdfAnnotations _parent; + + /// + /// Gets or sets the annotation rectangle, defining the location of the annotation + /// on the page in default user space units. + /// + public PdfRectangle Rectangle + { + get { return Elements.GetRectangle(Keys.Rect, true); } + set + { + Elements.SetRectangle(Keys.Rect, value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets the text label to be displayed in the title bar of the annotations + /// pop-up window when open and active. By convention, this entry identifies + /// the user who added the annotation. + /// + public string Title + { + get { return Elements.GetString(Keys.T, true); } + set + { + Elements.SetString(Keys.T, value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets text representing a short description of the subject being + /// addressed by the annotation. + /// + public string Subject + { + get { return Elements.GetString(Keys.Subj, true); } + set + { + Elements.SetString(Keys.Subj, value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets the text to be displayed for the annotation or, if this type of + /// annotation does not display text, an alternate description of the annotations + /// contents in human-readable form. + /// + public string Contents + { + get { return Elements.GetString(Keys.Contents, true); } + set + { + Elements.SetString(Keys.Contents, value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets the color representing the components of the annotation. If the color + /// has an alpha value other than 1, it is ignored. Use property Opacity to get or set the + /// opacity of an annotation. + /// + public XColor Color + { + get + { + PdfItem item = Elements[Keys.C]; + PdfArray array = item as PdfArray; + if (array != null) // TODO: check for iref? + { + if (array.Elements.Count == 3) + { + // TODO: an array.GetColor() function may be useful here + return XColor.FromArgb( + (int)(array.Elements.GetReal(0) * 255), + (int)(array.Elements.GetReal(1) * 255), + (int)(array.Elements.GetReal(2) * 255)); + } + } + return XColors.Black; + } + set + { + // TODO: an array.SetColor(clr) function may be useful here + PdfArray array = new PdfArray(Owner, new PdfReal[] { new PdfReal(value.R / 255.0), new PdfReal(value.G / 255.0), new PdfReal(value.B / 255.0) }); + Elements[Keys.C] = array; + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Gets or sets the constant opacity value to be used in painting the annotation. + /// This value applies to all visible elements of the annotation in its closed state + /// (including its background and border) but not to the popup window that appears when + /// the annotation is opened. + /// + public double Opacity + { + get + { + if (!Elements.ContainsKey(Keys.CA)) + return 1; + return Elements.GetReal(Keys.CA, true); + } + set + { + if (value < 0 || value > 1) + throw new ArgumentOutOfRangeException("value", value, "Opacity must be a value in the range from 0 to 1."); + Elements.SetReal(Keys.CA, value); + Elements.SetDateTime(Keys.M, DateTime.Now); + } + } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be Annot for an annotation dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Annot")] + public const string Type = "/Type"; + + /// + /// (Required) The type of annotation that this dictionary describes. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Subtype = "/Subtype"; + + /// + /// (Required) The annotation rectangle, defining the location of the annotation + /// on the page in default user space units. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Required)] + public const string Rect = "/Rect"; + + /// + /// (Optional) Text to be displayed for the annotation or, if this type of annotation + /// does not display text, an alternate description of the annotations contents + /// in human-readable form. In either case, this text is useful when + /// extracting the documents contents in support of accessibility to users with + /// disabilities or for other purposes. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Contents = "/Contents"; + + // P + + /// + /// (Optional; PDF 1.4) The annotation name, a text string uniquely identifying it + /// among all the annotations on its page. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string NM = "/NM"; + + /// + /// (Optional; PDF 1.1) The date and time when the annotation was most recently + /// modified. The preferred format is a date string, but viewer applications should be + /// prepared to accept and display a string in any format. + /// + [KeyInfo(KeyType.Date | KeyType.Optional)] + public const string M = "/M"; + + /// + /// (Optional; PDF 1.1) A set of flags specifying various characteristics of the annotation. + /// Default value: 0. + /// + [KeyInfo("1.1", KeyType.Integer | KeyType.Optional)] + public const string F = "/F"; + + /// + /// (Optional; PDF 1.2) A border style dictionary specifying the characteristics of + /// the annotations border. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional)] + public const string BS = "/BS"; + + /// + /// (Optional; PDF 1.2) An appearance dictionary specifying how the annotation + /// is presented visually on the page. Individual annotation handlers may ignore + /// this entry and provide their own appearances. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional)] + public const string AP = "/AP"; + + /// + /// (Required if the appearance dictionary AP contains one or more subdictionaries; PDF 1.2) + /// The annotations appearance state, which selects the applicable appearance stream from + /// an appearance subdictionary. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional)] + public const string AS = "/AS"; + + /// + /// (Optional) An array specifying the characteristics of the annotations border. + /// The border is specified as a rounded rectangle. + /// In PDF 1.0, the array consists of three numbers defining the horizontal corner + /// radius, vertical corner radius, and border width, all in default user space units. + /// If the corner radii are 0, the border has square (not rounded) corners; if the border + /// width is 0, no border is drawn. + /// In PDF 1.1, the array may have a fourth element, an optional dash array defining a + /// pattern of dashes and gaps to be used in drawing the border. The dash array is + /// specified in the same format as in the line dash pattern parameter of the graphics state. + /// For example, a Border value of [0 0 1 [3 2]] specifies a border 1 unit wide, with + /// square corners, drawn with 3-unit dashes alternating with 2-unit gaps. Note that no + /// dash phase is specified; the phase is assumed to be 0. + /// Note: In PDF 1.2 or later, this entry may be ignored in favor of the BS entry. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string Border = "/Border"; + + /// + /// (Optional; PDF 1.1) An array of three numbers in the range 0.0 to 1.0, representing + /// the components of a color in the DeviceRGB color space. This color is used for the + /// following purposes: + /// The background of the annotations icon when closed + /// The title bar of the annotations pop-up window + /// The border of a link annotation + /// + [KeyInfo("1.1", KeyType.Array | KeyType.Optional)] + public const string C = "/C"; + + // @PDF/UA + /// + /// (Required if the annotation is a structural content item; PDF 1.3) + /// The integer key of the annotations entry in the structural parent tree. + /// + [KeyInfo("1.3", KeyType.Integer | KeyType.Optional)] + public const string StructParent = "/StructParent"; + + /// + /// (Optional; PDF 1.1) An action to be performed when the annotation is activated. + /// Note: This entry is not permitted in link annotations if a Dest entry is present. + /// Also note that the A entry in movie annotations has a different meaning. + /// + [KeyInfo("1.1", KeyType.Dictionary | KeyType.Optional)] + public const string A = "/A"; + + // AA + // StructParent + // OC + + // ----- Excerpt of entries specific to markup annotations ---------------------------------- + + /// + /// (Optional; PDF 1.1) The text label to be displayed in the title bar of the annotations + /// pop-up window when open and active. By convention, this entry identifies + /// the user who added the annotation. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string T = "/T"; + + /// + /// (Optional; PDF 1.3) An indirect reference to a pop-up annotation for entering or + /// editing the text associated with this annotation. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Popup = "/Popup"; + + /// + /// (Optional; PDF 1.4) The constant opacity value to be used in painting the annotation. + /// This value applies to all visible elements of the annotation in its closed state + /// (including its background and border) but not to the popup window that appears when + /// the annotation is opened. + /// The specified value is not used if the annotation has an appearance stream; in that + /// case, the appearance stream must specify any transparency. (However, if the viewer + /// regenerates the annotations appearance stream, it may incorporate the CA value + /// into the streams content.) + /// The implicit blend mode is Normal. + /// Default value: 1.0. + /// + [KeyInfo(KeyType.Real | KeyType.Optional)] + public const string CA = "/CA"; + + //RC + //CreationDate + //IRT + + /// + /// (Optional; PDF 1.5) Text representing a short description of the subject being + /// addressed by the annotation. + /// + [KeyInfo("1.5", KeyType.TextString | KeyType.Optional)] + public const string Subj = "/Subj"; + + //RT + //IT + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotations.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotations.cs new file mode 100644 index 00000000..abad5189 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfAnnotations.cs @@ -0,0 +1,229 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections; +using System.Text; +using System.IO; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents the annotations array of a page. + /// + public sealed class PdfAnnotations : PdfArray + { + internal PdfAnnotations(PdfDocument document) + : base(document) + { } + + internal PdfAnnotations(PdfArray array) + : base(array) + { } + + /// + /// Adds the specified annotation. + /// + /// The annotation. + public void Add(PdfAnnotation annotation) + { + annotation.Document = Owner; + Owner._irefTable.Add(annotation); + Elements.Add(annotation.Reference); + } + + /// + /// Removes an annotation from the document. + /// + public void Remove(PdfAnnotation annotation) + { + if (annotation.Owner != Owner) + throw new InvalidOperationException("The annotation does not belong to this document."); + + Owner.Internals.RemoveObject(annotation); + Elements.Remove(annotation.Reference); + } + + /// + /// Removes all the annotations from the current page. + /// + public void Clear() + { + for (int idx = Count - 1; idx >= 0; idx--) + Page.Annotations.Remove(_page.Annotations[idx]); + } + + //public void Insert(int index, PdfAnnotation annotation) + //{ + // annotation.Document = Document; + // annotations.Insert(index, annotation); + //} + + /// + /// Gets the number of annotations in this collection. + /// + public int Count + { + get { return Elements.Count; } + } + + /// + /// Gets the at the specified index. + /// + public PdfAnnotation this[int index] + { + get + { + PdfReference iref; + PdfDictionary dict; + PdfItem item = Elements[index]; + if ((iref = item as PdfReference) != null) + { + Debug.Assert(iref.Value is PdfDictionary, "Reference to dictionary expected."); + dict = (PdfDictionary)iref.Value; + } + else + { + Debug.Assert(item is PdfDictionary, "Dictionary expected."); + dict = (PdfDictionary)item; + } + PdfAnnotation annotation = dict as PdfAnnotation; + if (annotation == null) + { + annotation = new PdfGenericAnnotation(dict); + if (iref == null) + Elements[index] = annotation; + } + return annotation; + } + } + + //public PdfAnnotation this[int index] + //{ + // get + // { + // //DMH 6/7/06 + // //Broke this out to simplfy debugging + // //Use a generic annotation to access the Meta data + // //Assign this as the parent of the annotation + // PdfReference r = Elements[index] as PdfReference; + // PdfDictionary d = r.Value as PdfDictionary; + // PdfGenericAnnotation a = new PdfGenericAnnotation(d); + // a.Collection = this; + // return a; + // } + //} + + /// + /// Gets the page the annotations belongs to. + /// + internal PdfPage Page + { + get { return _page; } + set { _page = value; } + } + PdfPage _page; + + /// + /// Fixes the /P element in imported annotation. + /// + internal static void FixImportedAnnotation(PdfPage page) + { + PdfArray annots = page.Elements.GetArray(PdfPage.Keys.Annots); + if (annots != null) + { + int count = annots.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfDictionary annot = annots.Elements.GetDictionary(idx); + if (annot != null && annot.Elements.ContainsKey("/P")) + annot.Elements["/P"] = page.Reference; + } + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + public override IEnumerator GetEnumerator() + { + return (IEnumerator)new AnnotationsIterator(this); + } + // THHO4STLA: AnnotationsIterator: Implementation does not work http://forum.pdfsharp.net/viewtopic.php?p=3285#p3285 + // Code using the enumerator like this will crash: + //foreach (var annotation in page.Annotations) + //{ + // annotation.GetType(); + //} + + //!!!new 2015-10-15: use PdfItem instead of PdfAnnotation. + // TODO Should we change this to "public new IEnumerator GetEnumerator()"? + + class AnnotationsIterator : IEnumerator + { + public AnnotationsIterator(PdfAnnotations annotations) + { + _annotations = annotations; + _index = -1; + } + + public PdfItem/*PdfAnnotation*/ Current + { + get { return _annotations[_index]; } + } + + object IEnumerator.Current + { + get { return Current; } + } + + public bool MoveNext() + { + return ++_index < _annotations.Count; + } + + public void Reset() + { + _index = -1; + } + + public void Dispose() + { + //throw new NotImplementedException(); + } + + readonly PdfAnnotations _annotations; + int _index; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfGenericAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfGenericAnnotation.cs new file mode 100644 index 00000000..a475b2ed --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfGenericAnnotation.cs @@ -0,0 +1,64 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents a generic annotation. Used for annotation dictionaries unknown to PDFsharp. + /// + internal sealed class PdfGenericAnnotation : PdfAnnotation + { + //DMH 6/7/06 + //Make this public so we can use it in PdfAnnotations to + //get the Meta data from existings annotations. + public PdfGenericAnnotation(PdfDictionary dict) + : base(dict) + { } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAnnotation.Keys + { + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfLinkAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfLinkAnnotation.cs new file mode 100644 index 00000000..c188ac0f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfLinkAnnotation.cs @@ -0,0 +1,300 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf.Actions; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents a link annotation. + /// + public sealed class PdfLinkAnnotation : PdfAnnotation + { + // Just a hack to make MigraDoc work with this code. + enum LinkType + { + None, Document, NamedDestination, Web, File + } + + /// + /// Initializes a new instance of the class. + /// + public PdfLinkAnnotation() + { + _linkType = LinkType.None; + Elements.SetName(PdfAnnotation.Keys.Subtype, "/Link"); + } + + /// + /// Initializes a new instance of the class. + /// + public PdfLinkAnnotation(PdfDocument document) + : base(document) + { + _linkType = LinkType.None; + Elements.SetName(PdfAnnotation.Keys.Subtype, "/Link"); + } + + /// + /// Creates a link within the current document. + /// + /// The link area in default page coordinates. + /// The one-based destination page number. + public static PdfLinkAnnotation CreateDocumentLink(PdfRectangle rect, int destinationPage) + { + if (destinationPage < 1) + throw new ArgumentException("Invalid destination page in call to CreateDocumentLink: page number is one-based and must be 1 or higher.", "destinationPage"); + + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.Document; + link.Rectangle = rect; + link._destPage = destinationPage; + return link; + } + int _destPage; + LinkType _linkType; + string _url; + + /// + /// Creates a link within the current document using a named destination. + /// + /// The link area in default page coordinates. + /// The named destination's name. + public static PdfLinkAnnotation CreateDocumentLink(PdfRectangle rect, string destinationName) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.NamedDestination; + link.Rectangle = rect; + link._action = PdfGoToAction.CreateGoToAction(destinationName); + return link; + } + PdfAction _action; + + /// + /// Creates a link to an external PDF document using a named destination. + /// + /// The link area in default page coordinates. + /// The path to the target document. + /// The named destination's name in the target document. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfLinkAnnotation CreateDocumentLink(PdfRectangle rect, string documentPath, string destinationName, bool? newWindow = null) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.NamedDestination; + link.Rectangle = rect; + link._action = PdfRemoteGoToAction.CreateRemoteGoToAction(documentPath, destinationName, newWindow); + return link; + } + + /// + /// Creates a link to an embedded document. + /// + /// The link area in default page coordinates. + /// The path to the named destination through the embedded documents. + /// The path is separated by '\' and the last segment is the name of the named destination. + /// The other segments describe the route from the current (root or embedded) document to the embedded document holding the destination. + /// ".." references to the parent, other strings refer to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfLinkAnnotation CreateEmbeddedDocumentLink(PdfRectangle rect, string destinationPath, bool? newWindow = null) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.NamedDestination; + link.Rectangle = rect; + link._action = PdfEmbeddedGoToAction.CreatePdfEmbeddedGoToAction(destinationPath, newWindow); + return link; + } + + /// + /// Creates a link to an embedded document in another document. + /// + /// The link area in default page coordinates. + /// The path to the target document. + /// The path to the named destination through the embedded documents in the target document. + /// The path is separated by '\' and the last segment is the name of the named destination. + /// The other segments describe the route from the root document to the embedded document. + /// Each segment name refers to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public static PdfLinkAnnotation CreateEmbeddedDocumentLink(PdfRectangle rect, string documentPath, string destinationPath, bool? newWindow = null) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.NamedDestination; + link.Rectangle = rect; + link._action = PdfEmbeddedGoToAction.CreatePdfEmbeddedGoToAction(documentPath, destinationPath, newWindow); + return link; + } + + /// + /// Creates a link to the web. + /// + public static PdfLinkAnnotation CreateWebLink(PdfRectangle rect, string url) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = PdfLinkAnnotation.LinkType.Web; + link.Rectangle = rect; + link._url = url; + return link; + } + + /// + /// Creates a link to a file. + /// + public static PdfLinkAnnotation CreateFileLink(PdfRectangle rect, string fileName) + { + PdfLinkAnnotation link = new PdfLinkAnnotation(); + link._linkType = LinkType.File; + // TODO: Adjust bleed box here (if possible) + link.Rectangle = rect; + link._url = fileName; + return link; + } + + internal override void WriteObject(PdfWriter writer) + { + PdfPage dest = null; + //pdf.AppendFormat(CultureInfo.InvariantCulture, + // "{0} 0 obj\n<<\n/Type/Annot\n/Subtype/Link\n" + + // "/Rect[{1} {2} {3} {4}]\n/BS<>\n/Border[0 0 0]\n/C[0 0 0]\n", + // ObjectID.ObjectNumber, rect.X1, rect.Y1, rect.X2, rect.Y2); + + // Older Adobe Reader versions uses a border width of 0 as default value if neither Border nor BS are present. + // But the PDF Reference specifies: + // "If neither the Border nor the BS entry is present, the border is drawn as a solid line with a width of 1 point." + // After this issue was fixed in newer Reader versions older PDFsharp created documents show an ugly solid border. + // The following hack fixes this by specifying a 0 width border. + if (Elements[PdfAnnotation.Keys.BS] == null) + Elements[PdfAnnotation.Keys.BS] = new PdfLiteral("<>"); + + // May be superfluous. See comment above. + if (Elements[PdfAnnotation.Keys.Border] == null) + Elements[PdfAnnotation.Keys.Border] = new PdfLiteral("[0 0 0]"); + + switch (_linkType) + { + case LinkType.None: + break; + + case LinkType.Document: + // destIndex > Owner.PageCount can happen when rendering pages using PDFsharp directly. + int destIndex = _destPage; + if (destIndex > Owner.PageCount) + destIndex = Owner.PageCount; + destIndex--; + dest = Owner.Pages[destIndex]; + //pdf.AppendFormat("/Dest[{0} 0 R/XYZ null null 0]\n", dest.ObjectID); + Elements[Keys.Dest] = new PdfLiteral("[{0} 0 R/XYZ null null 0]", dest.ObjectNumber); + break; + + case LinkType.NamedDestination: + Elements[PdfAnnotation.Keys.A] = _action; + break; + + case LinkType.Web: + //pdf.AppendFormat("/A<>\n", PdfEncoders.EncodeAsLiteral(url)); + Elements[PdfAnnotation.Keys.A] = new PdfLiteral("<>", //PdfEncoders.EncodeAsLiteral(url)); + PdfEncoders.ToStringLiteral(_url, PdfStringEncoding.WinAnsiEncoding, writer.SecurityHandler)); + break; + + case LinkType.File: + //pdf.AppendFormat("/A<> >>\n", + // PdfEncoders.EncodeAsLiteral(url)); + Elements[PdfAnnotation.Keys.A] = new PdfLiteral("<> >>", + //PdfEncoders.EncodeAsLiteral(url)); + PdfEncoders.ToStringLiteral(_url, PdfStringEncoding.WinAnsiEncoding, writer.SecurityHandler)); + break; + } + base.WriteObject(writer); + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAnnotation.Keys + { + // /// + // /// (Required) The type of annotation that this dictionary describes; + // /// must be Link for a link annotation. + // /// + // inherited from base class + + /// + /// (Optional; not permitted if an A entry is present) A destination to be displayed + /// when the annotation is activated. + /// + [KeyInfo(KeyType.ArrayOrNameOrString | KeyType.Optional)] + public const string Dest = "/Dest"; + + /// + /// (Optional; PDF 1.2) The annotations highlighting mode, the visual effect to be + /// used when the mouse button is pressed or held down inside its active area: + /// N (None) No highlighting. + /// I (Invert) Invert the contents of the annotation rectangle. + /// O (Outline) Invert the annotations border. + /// P (Push) Display the annotation as if it were being pushed below the surface of the page. + /// Default value: I. + /// Note: In PDF 1.1, highlighting is always done by inverting colors inside the annotation rectangle. + /// + [KeyInfo("1.2", KeyType.Name | KeyType.Optional)] + public const string H = "/H"; + + /// + /// (Optional; PDF 1.3) A URI action formerly associated with this annotation. When Web + /// Capture changes and annotation from a URI to a go-to action, it uses this entry to save + /// the data from the original URI action so that it can be changed back in case the target page for + /// the go-to action is subsequently deleted. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional)] + public const string PA = "/PA"; + + // QuadPoints + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfRubberStampAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfRubberStampAnnotation.cs new file mode 100644 index 00000000..cf75e9d8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfRubberStampAnnotation.cs @@ -0,0 +1,133 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents a rubber stamp annotation. + /// + public sealed class PdfRubberStampAnnotation : PdfAnnotation + { + /// + /// Initializes a new instance of the class. + /// + public PdfRubberStampAnnotation() + { + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfRubberStampAnnotation(PdfDocument document) + : base(document) + { + Initialize(); + } + + void Initialize() + { + Elements.SetName(Keys.Subtype, "/Stamp"); + Color = XColors.Yellow; + } + + /// + /// Gets or sets an icon to be used in displaying the annotation. + /// + public PdfRubberStampAnnotationIcon Icon + { + get + { + string value = Elements.GetName(Keys.Name); + if (value == "") + return PdfRubberStampAnnotationIcon.NoIcon; + value = value.Substring(1); + if (!Enum.IsDefined(typeof(PdfRubberStampAnnotationIcon), value)) + return PdfRubberStampAnnotationIcon.NoIcon; + return (PdfRubberStampAnnotationIcon)Enum.Parse(typeof(PdfRubberStampAnnotationIcon), value, false); + } + set + { + if (Enum.IsDefined(typeof(PdfRubberStampAnnotationIcon), value) && + PdfRubberStampAnnotationIcon.NoIcon != value) + { + Elements.SetName(Keys.Name, "/" + value.ToString()); + } + else + Elements.Remove(Keys.Name); + } + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAnnotation.Keys + { + /// + /// (Optional) The name of an icon to be used in displaying the annotation. Viewer + /// applications should provide predefined icon appearances for at least the following + /// standard names: + /// Approved + /// AsIs + /// Confidential + /// Departmental + /// Draft + /// Experimental + /// Expired + /// Final + /// ForComment + /// ForPublicRelease + /// NotApproved + /// NotForPublicRelease + /// Sold + /// TopSecret + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Name = "/Name"; + + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfTextAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfTextAnnotation.cs new file mode 100644 index 00000000..bc081744 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfTextAnnotation.cs @@ -0,0 +1,154 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents a text annotation. + /// + public sealed class PdfTextAnnotation : PdfAnnotation + { + /// + /// Initializes a new instance of the class. + /// + public PdfTextAnnotation() + { + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + public PdfTextAnnotation(PdfDocument document) + : base(document) + { + Initialize(); + } + + void Initialize() + { + Elements.SetName(Keys.Subtype, "/Text"); + // By default make a yellow comment. + Icon = PdfTextAnnotationIcon.Comment; + //Color = XColors.Yellow; + } + + // public static PdfTextAnnotation CreateDocumentLink(PdfRectangle rect, int destinatinPage) + // { + // PdfTextAnnotation link = new PdfTextAnnotation(); + // //link.linkType = PdfTextAnnotation.LinkType.Document; + // //link.Rectangle = rect; + // //link.destPage = destinatinPage; + // return link; + // } + + /// + /// Gets or sets a flag indicating whether the annotation should initially be displayed open. + /// + public bool Open + { + get { return Elements.GetBoolean(Keys.Open); } + set { Elements.SetBoolean(Keys.Open, value); } + } + + /// + /// Gets or sets an icon to be used in displaying the annotation. + /// + public PdfTextAnnotationIcon Icon + { + get + { + string value = Elements.GetName(Keys.Name); + if (value == "") + return PdfTextAnnotationIcon.NoIcon; + value = value.Substring(1); + if (!Enum.IsDefined(typeof(PdfTextAnnotationIcon), value)) + return PdfTextAnnotationIcon.NoIcon; + return (PdfTextAnnotationIcon)Enum.Parse(typeof(PdfTextAnnotationIcon), value, false); + } + set + { + if (Enum.IsDefined(typeof(PdfTextAnnotationIcon), value) && + PdfTextAnnotationIcon.NoIcon != value) + { + Elements.SetName(Keys.Name, "/" + value.ToString()); + } + else + Elements.Remove(Keys.Name); + } + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAnnotation.Keys + { + /// + /// (Optional) A flag specifying whether the annotation should initially be displayed open. + /// Default value: false (closed). + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string Open = "/Open"; + + /// + /// (Optional) The name of an icon to be used in displaying the annotation. Viewer + /// applications should provide predefined icon appearances for at least the following + /// standard names: + /// Comment + /// Help + /// Insert + /// Key + /// NewParagraph + /// Note + /// Paragraph + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Name = "/Name"; + + //State + //StateModel + + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfWidgetAnnotation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfWidgetAnnotation.cs new file mode 100644 index 00000000..8ba8be24 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/PdfWidgetAnnotation.cs @@ -0,0 +1,97 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Represents a text annotation. + /// + internal sealed class PdfWidgetAnnotation : PdfAnnotation + { + public PdfWidgetAnnotation() + { + Initialize(); + } + + public PdfWidgetAnnotation(PdfDocument document) + : base(document) + { + Initialize(); + } + + void Initialize() + { + Elements.SetName(Keys.Subtype, "/Widget"); + } + + /// + /// Predefined keys of this dictionary. + /// + internal new class Keys : PdfAnnotation.Keys + { + /// + /// (Optional) The annotations highlighting mode, the visual effect to be used when + /// the mouse button is pressed or held down inside its active area: + /// N (None) No highlighting. + /// I (Invert) Invert the contents of the annotation rectangle. + /// O (Outline) Invert the annotations border. + /// P (Push) Display the annotations down appearance, if any. If no down appearance is defined, + /// offset the contents of the annotation rectangle to appear as if it were being pushed below + /// the surface of the page. + /// T (Toggle) Same as P (which is preferred). + /// A highlighting mode other than P overrides any down appearance defined for the annotation. + /// Default value: I. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string H = "/H"; + + /// + /// (Optional) An appearance characteristics dictionary to be used in constructing a dynamic + /// appearance stream specifying the annotations visual presentation on the page. + /// The name MK for this entry is of historical significance only and has no direct meaning. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string MK = "/MK"; + + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfAnnotationFlags.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfAnnotationFlags.cs new file mode 100644 index 00000000..81e9edcb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfAnnotationFlags.cs @@ -0,0 +1,112 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Specifies the annotation flags. + /// + [System.Flags] + public enum PdfAnnotationFlags + { + /// + /// If set, do not display the annotation if it does not belong to one of the standard + /// annotation types and no annotation handler is available. If clear, display such an + /// unknown annotation using an appearance stream specified by its appearancedictionary, + /// if any. + /// + Invisible = 1 << (1 - 1), + + /// + /// (PDF 1.2) If set, do not display or print the annotation or allow it to interact + /// with the user, regardless of its annotation type or whether an annotation + /// handler is available. In cases where screen space is limited, the ability to hide + /// and show annotations selectively can be used in combination with appearance + /// streams to display auxiliary pop-up information similar in function to online + /// help systems. + /// + Hidden = 1 << (2 - 1), + + /// + /// (PDF 1.2) If set, print the annotation when the page is printed. If clear, never + /// print the annotation, regardless of whether it is displayed on the screen. This + /// can be useful, for example, for annotations representing interactive pushbuttons, + /// which would serve no meaningful purpose on the printed page. + /// + Print = 1 << (3 - 1), + + /// + /// (PDF 1.3) If set, do not scale the annotations appearance to match the magnification + /// of the page. The location of the annotation on the page (defined by the + /// upper-left corner of its annotation rectangle) remains fixed, regardless of the + /// page magnification. See below for further discussion. + /// + NoZoom = 1 << (4 - 1), + + /// + /// (PDF 1.3) If set, do not rotate the annotations appearance to match the rotation + /// of the page. The upper-left corner of the annotation rectangle remains in a fixed + /// location on the page, regardless of the page rotation. See below for further discussion. + /// + NoRotate = 1 << (5 - 1), + + /// + /// (PDF 1.3) If set, do not display the annotation on the screen or allow it to + /// interact with the user. The annotation may be printed (depending on the setting + /// of the Print flag) but should be considered hidden for purposes of on-screen + /// display and user interaction. + /// + NoView = 1 << (6 - 1), + + /// + /// (PDF 1.3) If set, do not allow the annotation to interact with the user. The + /// annotation may be displayed or printed (depending on the settings of the + /// NoView and Print flags) but should not respond to mouse clicks or change its + /// appearance in response to mouse motions. + /// Note: This flag is ignored for widget annotations; its function is subsumed by + /// the ReadOnly flag of the associated form field. + /// + ReadOnly = 1 << (7 - 1), + + /// + /// (PDF 1.4) If set, do not allow the annotation to be deleted or its properties + /// (including position and size) to be modified by the user. However, this flag does + /// not restrict changes to the annotations contents, such as the value of a form + /// field. + /// + Locked = 1 << (8 - 1), + + /// + /// (PDF 1.5) If set, invert the interpretation of the NoView flag for certain events. + /// A typical use is to have an annotation that appears only when a mouse cursor is + /// held over it. + /// + ToggleNoView = 1 << (9 - 1), + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfRubberStampAnnotationIcon.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfRubberStampAnnotationIcon.cs new file mode 100644 index 00000000..7e6a9042 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfRubberStampAnnotationIcon.cs @@ -0,0 +1,112 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Specifies the predefined icon names of rubber stamp annotations. + /// + public enum PdfRubberStampAnnotationIcon + { + /// + /// A pre-defined rubber stamp annotation icon. + /// + NoIcon, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Approved, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + AsIs, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Confidential, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Departmental, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Draft, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Experimental, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Expired, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Final, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + ForComment, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + ForPublicRelease, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + NotApproved, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + NotForPublicRelease, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + Sold, + + /// + /// A pre-defined rubber stamp annotation icon. + /// + TopSecret, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfTextAnnotationIcon.cs b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfTextAnnotationIcon.cs new file mode 100644 index 00000000..4dbb157b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Annotations/enums/PdfTextAnnotationIcon.cs @@ -0,0 +1,77 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Annotations +{ + /// + /// Specifies the pre-defined icon names of text annotations. + /// + public enum PdfTextAnnotationIcon + { + /// + /// A pre-defined annotation icon. + /// + NoIcon, + + /// + /// A pre-defined annotation icon. + /// + Comment, + + /// + /// A pre-defined annotation icon. + /// + Help, + + /// + /// A pre-defined annotation icon. + /// + Insert, + + /// + /// A pre-defined annotation icon. + /// + Key, + + /// + /// A pre-defined annotation icon. + /// + NewParagraph, + + /// + /// A pre-defined annotation icon. + /// + Note, + + /// + /// A pre-defined annotation icon. + /// + Paragraph, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/CObjects.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/CObjects.cs new file mode 100644 index 00000000..44308a02 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/CObjects.cs @@ -0,0 +1,919 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; + +namespace PdfSharp.Pdf.Content.Objects // TODO: split into single files +{ + /// + /// Base class for all PDF content stream objects. + /// + public abstract class CObject : ICloneable + { + /// + /// Initializes a new instance of the class. + /// + protected CObject() + { } + + /// + /// Creates a new object that is a copy of the current instance. + /// + object ICloneable.Clone() + { + return Copy(); + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + public CObject Clone() + { + return Copy(); + } + + /// + /// Implements the copy mechanism. Must be overridden in derived classes. + /// + protected virtual CObject Copy() + { + return (CObject)MemberwiseClone(); + } + + /// + /// + /// + internal abstract void WriteObject(ContentWriter writer); + } + + /// + /// Represents a comment in a PDF content stream. + /// + [DebuggerDisplay("({Text})")] + public class CComment : CObject + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CComment Clone() + { + return (CComment)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the comment text. + /// + public string Text + { + get { return _text; } + set { _text = value; } + } + string _text; + + /// + /// Returns a string that represents the current comment. + /// + public override string ToString() + { + return "% " + _text; + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteLineRaw(ToString()); + } + } + + /// + /// Represents a sequence of objects in a PDF content stream. + /// + [DebuggerDisplay("(count={Count})")] + public class CSequence : CObject, IList // , ICollection, IEnumerable + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CSequence Clone() + { + return (CSequence)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + _items = new List(_items); + for (int idx = 0; idx < _items.Count; idx++) + _items[idx] = _items[idx].Clone(); + return obj; + } + + /// + /// Adds the specified sequence. + /// + /// The sequence. + public void Add(CSequence sequence) + { + int count = sequence.Count; + for (int idx = 0; idx < count; idx++) + _items.Add(sequence[idx]); + } + + #region IList Members + + /// + /// Adds the specified value add the end of the sequence. + /// + public void Add(CObject value) + { + _items.Add(value); + } + + /// + /// Removes all elements from the sequence. + /// + public void Clear() + { + _items.Clear(); + } + + //bool IList.Contains(object value) + //{ + // return items.Contains(value); + //} + + /// + /// Determines whether the specified value is in the sequence. + /// + public bool Contains(CObject value) + { + return _items.Contains(value); + } + + /// + /// Returns the index of the specified value in the sequence or -1, if no such value is in the sequence. + /// + public int IndexOf(CObject value) + { + return _items.IndexOf(value); + } + + /// + /// Inserts the specified value in the sequence. + /// + public void Insert(int index, CObject value) + { + _items.Insert(index, value); + } + + /////// + /////// Gets a value indicating whether the sequence has a fixed size. + /////// + ////public bool IsFixedSize + ////{ + //// get { return items.IsFixedSize; } + ////} + + /////// + /////// Gets a value indicating whether the sequence is read-only. + /////// + ////public bool IsReadOnly + ////{ + //// get { return items.IsReadOnly; } + ////} + + /// + /// Removes the specified value from the sequence. + /// + public bool Remove(CObject value) + { + return _items.Remove(value); + } + + /// + /// Removes the value at the specified index from the sequence. + /// + public void RemoveAt(int index) + { + _items.RemoveAt(index); + } + + /// + /// Gets or sets a CObject at the specified index. + /// + /// + public CObject this[int index] + { + get { return (CObject)_items[index]; } + set { _items[index] = value; } + } + #endregion + + #region ICollection Members + + /// + /// Copies the elements of the sequence to the specified array. + /// + public void CopyTo(CObject[] array, int index) + { + _items.CopyTo(array, index); + } + + + /// + /// Gets the number of elements contained in the sequence. + /// + public int Count + { + get { return _items.Count; } + } + + ///// + ///// Gets a value indicating whether access to the sequence is synchronized (thread safe). + ///// + //public bool IsSynchronized + //{ + // get { return items.IsSynchronized; } + //} + + ///// + ///// Gets an object that can be used to synchronize access to the sequence. + ///// + //public object SyncRoot + //{ + // get { return items.SyncRoot; } + //} + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that iterates through the sequence. + /// + public IEnumerator GetEnumerator() + { + return _items.GetEnumerator(); + } + + #endregion + + /// + /// Converts the sequence to a PDF content stream. + /// + public byte[] ToContent() + { + Stream stream = new MemoryStream(); + ContentWriter writer = new ContentWriter(stream); + WriteObject(writer); + writer.Close(false); + + stream.Position = 0; + int count = (int)stream.Length; + byte[] bytes = new byte[count]; + stream.Read(bytes, 0, count); +#if !UWP + stream.Close(); +#else + stream.Dispose(); +#endif + return bytes; + } + + /// + /// Returns a string containing all elements of the sequence. + /// + public override string ToString() + { + StringBuilder s = new StringBuilder(); + + for (int idx = 0; idx < _items.Count; idx++) + s.Append(_items[idx]); + + return s.ToString(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal override void WriteObject(ContentWriter writer) + { + for (int idx = 0; idx < _items.Count; idx++) + _items[idx].WriteObject(writer); + } + + #region IList Members + + int IList.IndexOf(CObject item) + { + throw new NotImplementedException(); + } + + void IList.Insert(int index, CObject item) + { + throw new NotImplementedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotImplementedException(); + } + + CObject IList.this[int index] + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + #endregion + + #region ICollection Members + + void ICollection.Add(CObject item) + { + throw new NotImplementedException(); + } + + void ICollection.Clear() + { + throw new NotImplementedException(); + } + + bool ICollection.Contains(CObject item) + { + throw new NotImplementedException(); + } + + void ICollection.CopyTo(CObject[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + int ICollection.Count + { + get { throw new NotImplementedException(); } + } + + bool ICollection.IsReadOnly + { + get { throw new NotImplementedException(); } + } + + bool ICollection.Remove(CObject item) + { + throw new NotImplementedException(); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + #endregion + + List _items = new List(); + } + + /// + /// Represents the base class for numerical objects in a PDF content stream. + /// + public abstract class CNumber : CObject + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CNumber Clone() + { + return (CNumber)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + //internal override void WriteObject(ContentWriter writer) + //{ + // throw new Exception("Must not come here."); + //} + } + + /// + /// Represents an integer value in a PDF content stream. + /// + [DebuggerDisplay("({Value})")] + public class CInteger : CNumber + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CInteger Clone() + { + return (CInteger)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the value. + /// + public int Value + { + get { return _value; } + set { _value = value; } + } + int _value; + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + return _value.ToString(CultureInfo.InvariantCulture); + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteRaw(ToString() + " "); + } + } + + /// + /// Represents a real value in a PDF content stream. + /// + [DebuggerDisplay("({Value})")] + public class CReal : CNumber + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CReal Clone() + { + return (CReal)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the value. + /// + public double Value + { + get { return _value; } + set { _value = value; } + } + double _value; + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + const string format = Config.SignificantFigures1Plus9; + return _value.ToString(format, CultureInfo.InvariantCulture); + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteRaw(ToString() + " "); + } + } + + /// + /// Type of the parsed string. + /// + public enum CStringType + { + /// + /// The string has the format "(...)". + /// + String, + + /// + /// The string has the format "<...>". + /// + HexString, + + /// + /// The string... TODO. + /// + UnicodeString, + + /// + /// The string... TODO. + /// + UnicodeHexString, + + /// + /// HACK: The string is the content of a dictionary. + /// Currently there is no parser for dictionaries in Content Streams. + /// + Dictionary, + } + + /// + /// Represents a string value in a PDF content stream. + /// + [DebuggerDisplay("({Value})")] + public class CString : CObject + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CString Clone() + { + return (CString)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the value. + /// + public string Value + { + get { return _value; } + set { _value = value; } + } + string _value; + + /// + /// Gets or sets the type of the content string. + /// + public CStringType CStringType + { + get { return _cStringType; } + set { _cStringType = value; } + } + CStringType _cStringType; + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + StringBuilder s = new StringBuilder(); + switch (CStringType) + { + case CStringType.String: + s.Append("("); + int length = _value.Length; + for (int ich = 0; ich < length; ich++) + { + char ch = _value[ich]; + switch (ch) + { + case Chars.LF: + s.Append("\\n"); + break; + + case Chars.CR: + s.Append("\\r"); + break; + + case Chars.HT: + s.Append("\\t"); + break; + + case Chars.BS: + s.Append("\\b"); + break; + + case Chars.FF: + s.Append("\\f"); + break; + + case Chars.ParenLeft: + s.Append("\\("); + break; + + case Chars.ParenRight: + s.Append("\\)"); + break; + + case Chars.BackSlash: + s.Append("\\\\"); + break; + + default: +#if true_ + // not absolut necessary to use octal encoding for characters less than blank + if (ch < ' ') + { + s.Append("\\"); + s.Append((char)(((ch >> 6) & 7) + '0')); + s.Append((char)(((ch >> 3) & 7) + '0')); + s.Append((char)((ch & 7) + '0')); + } + else +#endif + s.Append(ch); + break; + } + } + s.Append(')'); + break; + + + case CStringType.HexString: + throw new NotImplementedException(); + //break; + + case CStringType.UnicodeString: + throw new NotImplementedException(); + //break; + + case CStringType.UnicodeHexString: + throw new NotImplementedException(); + //break; + + case CStringType.Dictionary: + s.Append(_value); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + return s.ToString(); + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteRaw(ToString()); + } + } + + /// + /// Represents a name in a PDF content stream. + /// + [DebuggerDisplay("({Name})")] + public class CName : CObject + { + /// + /// Initializes a new instance of the class. + /// + public CName() + { + _name = "/"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name. + public CName(string name) + { + Name = name; + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CName Clone() + { + return (CName)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the name. Names must start with a slash. + /// + public string Name + { + get { return _name; } + set + { + if (String.IsNullOrEmpty(_name)) + throw new ArgumentNullException(nameof(value)); + if (_name[0] != '/') + throw new ArgumentException(PSSR.NameMustStartWithSlash); + _name = value; + } + } + string _name; + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + return _name; + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteRaw(ToString() + " "); + } + } + + /// + /// Represents an array of objects in a PDF content stream. + /// + [DebuggerDisplay("(count={Count})")] + public class CArray : CSequence + { + /// + /// Creates a new object that is a copy of the current instance. + /// + public new CArray Clone() + { + return (CArray)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + return "[" + base.ToString() + "]"; + } + + internal override void WriteObject(ContentWriter writer) + { + writer.WriteRaw(ToString()); + } + } + + /// + /// Represents an operator a PDF content stream. + /// + [DebuggerDisplay("({Name}, operands={Operands.Count})")] + public class COperator : CObject + { + /// + /// Initializes a new instance of the class. + /// + protected COperator() + { } + + internal COperator(OpCode opcode) + { + _opcode = opcode; + } + + /// + /// Creates a new object that is a copy of the current instance. + /// + public new COperator Clone() + { + return (COperator)Copy(); + } + + /// + /// Implements the copy mechanism of this class. + /// + protected override CObject Copy() + { + CObject obj = base.Copy(); + return obj; + } + + /// + /// Gets or sets the name of the operator + /// + /// The name. + public virtual string Name + { + get { return _opcode.Name; } + } + + /// + /// Gets or sets the operands. + /// + /// The operands. + public CSequence Operands + { + get { return _seqence ?? (_seqence = new CSequence()); } + } + CSequence _seqence; + + /// + /// Gets the operator description for this instance. + /// + public OpCode OpCode + { + get { return _opcode; } + } + readonly OpCode _opcode; + + + /// + /// Returns a string that represents the current operator. + /// + public override string ToString() + { + if (_opcode.OpCodeName == OpCodeName.Dictionary) + return " "; + + return Name; + } + + internal override void WriteObject(ContentWriter writer) + { + int count = _seqence != null ? _seqence.Count : 0; + for (int idx = 0; idx < count; idx++) + { + // ReSharper disable once PossibleNullReferenceException because the loop is not entered if _sequence is null + _seqence[idx].WriteObject(writer); + } + writer.WriteLineRaw(ToString()); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/Operators.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/Operators.cs new file mode 100644 index 00000000..8629e5e1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/Operators.cs @@ -0,0 +1,367 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Content.Objects +{ + /// + /// Represents a PDF content stream operator description. + /// + public sealed class OpCode + { + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// The enum value of the operator. + /// The number of operands. + /// The postscript equivalent, or null, if no such operation exists. + /// The flags. + /// The description from Adobe PDF Reference. + internal OpCode(string name, OpCodeName opcodeName, int operands, string postscript, OpCodeFlags flags, string description) + { + Name = name; + OpCodeName = opcodeName; + Operands = operands; + Postscript = postscript; + Flags = flags; + Description = description; + } + + /// + /// The name of the operator. + /// + public readonly string Name; + + /// + /// The enum value of the operator. + /// + public readonly OpCodeName OpCodeName; + + /// + /// The number of operands. -1 indicates a variable number of operands. + /// + public readonly int Operands; + + /// + /// The flags. + /// + public readonly OpCodeFlags Flags; + + /// + /// The postscript equivalent, or null, if no such operation exists. + /// + public readonly string Postscript; + + /// + /// The description from Adobe PDF Reference. + /// + public readonly string Description; + } + + /// + /// Static class with all PDF op-codes. + /// + public static class OpCodes + { + /// + /// Operators from name. + /// + /// The name. + public static COperator OperatorFromName(string name) + { + COperator op = null; + OpCode opcode = StringToOpCode[name]; + if (opcode != null) + { + op = new COperator(opcode); + } + else + { + Debug.Assert(false, "Unknown operator in PDF content stream."); + } + return op; + } + + /// + /// Initializes the class. + /// + static OpCodes() + { + StringToOpCode = new Dictionary(); + for (int idx = 0; idx < ops.Length; idx++) + { + OpCode op = ops[idx]; + StringToOpCode.Add(op.Name, op); + } + } + static readonly Dictionary StringToOpCode; + + // ReSharper disable InconsistentNaming + + static readonly OpCode Dictionary = new OpCode("Dictionary", OpCodeName.Dictionary, -1, "name, dictionary", OpCodeFlags.None, + "E.g.: /Name << ... >>"); + + static readonly OpCode b = new OpCode("b", OpCodeName.b, 0, "closepath, fill, stroke", OpCodeFlags.None, + "Close, fill, and stroke path using nonzero winding number"); + + static readonly OpCode B = new OpCode("B", OpCodeName.B, 0, "fill, stroke", OpCodeFlags.None, + "Fill and stroke path using nonzero winding number rule"); + + static readonly OpCode bx = new OpCode("b*", OpCodeName.bx, 0, "closepath, eofill, stroke", OpCodeFlags.None, + "Close, fill, and stroke path using even-odd rule"); + + static readonly OpCode Bx = new OpCode("B*", OpCodeName.Bx, 0, "eofill, stroke", OpCodeFlags.None, + "Fill and stroke path using even-odd rule"); + + static readonly OpCode BDC = new OpCode("BDC", OpCodeName.BDC, -1, null, OpCodeFlags.None, + "(PDF 1.2) Begin marked-content sequence with property list"); + + static readonly OpCode BI = new OpCode("BI", OpCodeName.BI, 0, null, OpCodeFlags.None, + "Begin inline image object"); + + static readonly OpCode BMC = new OpCode("BMC", OpCodeName.BMC, 1, null, OpCodeFlags.None, + "(PDF 1.2) Begin marked-content sequence"); + + static readonly OpCode BT = new OpCode("BT", OpCodeName.BT, 0, null, OpCodeFlags.None, + "Begin text object"); + + static readonly OpCode BX = new OpCode("BX", OpCodeName.BX, 0, null, OpCodeFlags.None, + "(PDF 1.1) Begin compatibility section"); + + static readonly OpCode c = new OpCode("c", OpCodeName.c, 6, "curveto", OpCodeFlags.None, + "Append curved segment to path (three control points)"); + + static readonly OpCode cm = new OpCode("cm", OpCodeName.cm, 6, "concat", OpCodeFlags.None, + "Concatenate matrix to current transformation matrix"); + + static readonly OpCode CS = new OpCode("CS", OpCodeName.CS, 1, "setcolorspace", OpCodeFlags.None, + "(PDF 1.1) Set color space for stroking operations"); + + static readonly OpCode cs = new OpCode("cs", OpCodeName.cs, 1, "setcolorspace", OpCodeFlags.None, + "(PDF 1.1) Set color space for nonstroking operations"); + + static readonly OpCode d = new OpCode("d", OpCodeName.d, 2, "setdash", OpCodeFlags.None, + "Set line dash pattern"); + + static readonly OpCode d0 = new OpCode("d0", OpCodeName.d0, 2, "setcharwidth", OpCodeFlags.None, + "Set glyph width in Type 3 font"); + + static readonly OpCode d1 = new OpCode("d1", OpCodeName.d1, 6, "setcachedevice", OpCodeFlags.None, + "Set glyph width and bounding box in Type 3 font"); + + static readonly OpCode Do = new OpCode("Do", OpCodeName.Do, 1, null, OpCodeFlags.None, + "Invoke named XObject"); + + static readonly OpCode DP = new OpCode("DP", OpCodeName.DP, 2, null, OpCodeFlags.None, + "(PDF 1.2) Define marked-content point with property list"); + + static readonly OpCode EI = new OpCode("EI", OpCodeName.EI, 0, null, OpCodeFlags.None, + "End inline image object"); + + static readonly OpCode EMC = new OpCode("EMC", OpCodeName.EMC, 0, null, OpCodeFlags.None, + "(PDF 1.2) End marked-content sequence"); + + static readonly OpCode ET = new OpCode("ET", OpCodeName.ET, 0, null, OpCodeFlags.None, + "End text object"); + + static readonly OpCode EX = new OpCode("EX", OpCodeName.EX, 0, null, OpCodeFlags.None, + "(PDF 1.1) End compatibility section"); + + static readonly OpCode f = new OpCode("f", OpCodeName.f, 0, "fill", OpCodeFlags.None, + "Fill path using nonzero winding number rule"); + + static readonly OpCode F = new OpCode("F", OpCodeName.F, 0, "fill", OpCodeFlags.None, + "Fill path using nonzero winding number rule (obsolete)"); + + static readonly OpCode fx = new OpCode("f*", OpCodeName.fx, 0, "eofill", OpCodeFlags.None, + "Fill path using even-odd rule"); + + static readonly OpCode G = new OpCode("G", OpCodeName.G, 1, "setgray", OpCodeFlags.None, + "Set gray level for stroking operations"); + + static readonly OpCode g = new OpCode("g", OpCodeName.g, 1, "setgray", OpCodeFlags.None, + "Set gray level for nonstroking operations"); + + static readonly OpCode gs = new OpCode("gs", OpCodeName.gs, 1, null, OpCodeFlags.None, + "(PDF 1.2) Set parameters from graphics state parameter dictionary"); + + static readonly OpCode h = new OpCode("h", OpCodeName.h, 0, "closepath", OpCodeFlags.None, + "Close subpath"); + + static readonly OpCode i = new OpCode("i", OpCodeName.i, 1, "setflat", OpCodeFlags.None, + "Set flatness tolerance"); + + static readonly OpCode ID = new OpCode("ID", OpCodeName.ID, 0, null, OpCodeFlags.None, + "Begin inline image data"); + + static readonly OpCode j = new OpCode("j", OpCodeName.j, 1, "setlinejoin", OpCodeFlags.None, + "Set line join style"); + + static readonly OpCode J = new OpCode("J", OpCodeName.J, 1, "setlinecap", OpCodeFlags.None, + "Set line cap style"); + + static readonly OpCode K = new OpCode("K", OpCodeName.K, 4, "setcmykcolor", OpCodeFlags.None, + "Set CMYK color for stroking operations"); + + static readonly OpCode k = new OpCode("k", OpCodeName.k, 4, "setcmykcolor", OpCodeFlags.None, + "Set CMYK color for nonstroking operations"); + + static readonly OpCode l = new OpCode("l", OpCodeName.l, 2, "lineto", OpCodeFlags.None, + "Append straight line segment to path"); + + static readonly OpCode m = new OpCode("m", OpCodeName.m, 2, "moveto", OpCodeFlags.None, + "Begin new subpath"); + + static readonly OpCode M = new OpCode("M", OpCodeName.M, 1, "setmiterlimit", OpCodeFlags.None, + "Set miter limit"); + + static readonly OpCode MP = new OpCode("MP", OpCodeName.MP, 1, null, OpCodeFlags.None, + "(PDF 1.2) Define marked-content point"); + + static readonly OpCode n = new OpCode("n", OpCodeName.n, 0, null, OpCodeFlags.None, + "End path without filling or stroking"); + + static readonly OpCode q = new OpCode("q", OpCodeName.q, 0, "gsave", OpCodeFlags.None, + "Save graphics state"); + + static readonly OpCode Q = new OpCode("Q", OpCodeName.Q, 0, "grestore", OpCodeFlags.None, + "Restore graphics state"); + + static readonly OpCode re = new OpCode("re", OpCodeName.re, 4, null, OpCodeFlags.None, + "Append rectangle to path"); + + static readonly OpCode RG = new OpCode("RG", OpCodeName.RG, 3, "setrgbcolor", OpCodeFlags.None, + "Set RGB color for stroking operations"); + + static readonly OpCode rg = new OpCode("rg", OpCodeName.rg, 3, "setrgbcolor", OpCodeFlags.None, + "Set RGB color for nonstroking operations"); + + static readonly OpCode ri = new OpCode("ri", OpCodeName.ri, 1, null, OpCodeFlags.None, + "Set color rendering intent"); + + static readonly OpCode s = new OpCode("s", OpCodeName.s, 0, "closepath,stroke", OpCodeFlags.None, + "Close and stroke path"); + + static readonly OpCode S = new OpCode("S", OpCodeName.S, 0, "stroke", OpCodeFlags.None, + "Stroke path"); + + static readonly OpCode SC = new OpCode("SC", OpCodeName.SC, -1, "setcolor", OpCodeFlags.None, + "(PDF 1.1) Set color for stroking operations"); + + static readonly OpCode sc = new OpCode("sc", OpCodeName.sc, -1, "setcolor", OpCodeFlags.None, + "(PDF 1.1) Set color for nonstroking operations"); + + static readonly OpCode SCN = new OpCode("SCN", OpCodeName.SCN, -1, "setcolor", OpCodeFlags.None, + "(PDF 1.2) Set color for stroking operations (ICCBased and special color spaces)"); + + static readonly OpCode scn = new OpCode("scn", OpCodeName.scn, -1, "setcolor", OpCodeFlags.None, + "(PDF 1.2) Set color for nonstroking operations (ICCBased and special color spaces)"); + + static readonly OpCode sh = new OpCode("sh", OpCodeName.sh, 1, "shfill", OpCodeFlags.None, + "(PDF 1.3) Paint area defined by shading pattern"); + + static readonly OpCode Tx = new OpCode("T*", OpCodeName.Tx, 0, null, OpCodeFlags.None, + "Move to start of next text line"); + + static readonly OpCode Tc = new OpCode("Tc", OpCodeName.Tc, 1, null, OpCodeFlags.None, + "Set character spacing"); + + static readonly OpCode Td = new OpCode("Td", OpCodeName.Td, 2, null, OpCodeFlags.None, + "Move text position"); + + static readonly OpCode TD = new OpCode("TD", OpCodeName.TD, 2, null, OpCodeFlags.None, + "Move text position and set leading"); + + static readonly OpCode Tf = new OpCode("Tf", OpCodeName.Tf, 2, "selectfont", OpCodeFlags.None, + "Set text font and size"); + + static readonly OpCode Tj = new OpCode("Tj", OpCodeName.Tj, 1, "show", OpCodeFlags.TextOut, + "Show text"); + + static readonly OpCode TJ = new OpCode("TJ", OpCodeName.TJ, 1, null, OpCodeFlags.TextOut, + "Show text, allowing individual glyph positioning"); + + static readonly OpCode TL = new OpCode("TL", OpCodeName.TL, 1, null, OpCodeFlags.None, + "Set text leading"); + + static readonly OpCode Tm = new OpCode("Tm", OpCodeName.Tm, 6, null, OpCodeFlags.None, + "Set text matrix and text line matrix"); + + static readonly OpCode Tr = new OpCode("Tr", OpCodeName.Tr, 1, null, OpCodeFlags.None, + "Set text rendering mode"); + + static readonly OpCode Ts = new OpCode("Ts", OpCodeName.Ts, 1, null, OpCodeFlags.None, + "Set text rise"); + + static readonly OpCode Tw = new OpCode("Tw", OpCodeName.Tw, 1, null, OpCodeFlags.None, + "Set word spacing"); + + static readonly OpCode Tz = new OpCode("Tz", OpCodeName.Tz, 1, null, OpCodeFlags.None, + "Set horizontal text scaling"); + + static readonly OpCode v = new OpCode("v", OpCodeName.v, 4, "curveto", OpCodeFlags.None, + "Append curved segment to path (initial point replicated)"); + + static readonly OpCode w = new OpCode("w", OpCodeName.w, 1, "setlinewidth", OpCodeFlags.None, + "Set line width"); + + static readonly OpCode W = new OpCode("W", OpCodeName.W, 0, "clip", OpCodeFlags.None, + "Set clipping path using nonzero winding number rule"); + + static readonly OpCode Wx = new OpCode("W*", OpCodeName.Wx, 0, "eoclip", OpCodeFlags.None, + "Set clipping path using even-odd rule"); + + static readonly OpCode y = new OpCode("y", OpCodeName.y, 4, "curveto", OpCodeFlags.None, + "Append curved segment to path (final point replicated)"); + + static readonly OpCode QuoteSingle = new OpCode("'", OpCodeName.QuoteSingle, 1, null, OpCodeFlags.TextOut, + "Move to next line and show text"); + + static readonly OpCode QuoteDbl = new OpCode("\"", OpCodeName.QuoteDbl, 3, null, OpCodeFlags.TextOut, + "Set word and character spacing, move to next line, and show text"); + + /// + /// Array of all OpCodes. + /// + static readonly OpCode[] ops = // new OpCode[] + { + // Must be defined behind the code above to ensure that the values are initialized. + Dictionary, + b, B, bx, Bx, BDC, BI, BMC, BT, BX, c, cm, CS, cs, d, d0, d1, Do, + DP, EI, EMC, ET, EX, f, F, fx, G, g, gs, h, i, ID, j, J, K, k, l, m, M, MP, + n, q, Q, re, RG, rg, ri, s, S, SC, sc, SCN, scn, sh, + Tx, Tc, Td, TD, Tf, Tj, TJ, TL, Tm, Tr, Ts, Tw, Tz, v, w, W, Wx, y, + QuoteSingle, QuoteDbl + }; + // ReSharper restore InconsistentNaming + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeFlags.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeFlags.cs new file mode 100644 index 00000000..6ac21d2f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeFlags.cs @@ -0,0 +1,51 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Content.Objects +{ + /// + /// Specifies the group of operations the op-code belongs to. + /// + [Flags] + public enum OpCodeFlags + { + /// + /// + /// + None, + + /// + /// + /// + TextOut = 0x0001, + //Color, Pattern, Images,... + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeName.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeName.cs new file mode 100644 index 00000000..96a76cb7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content.Objects/enum/OpCodeName.cs @@ -0,0 +1,185 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#pragma warning disable 1591 + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Pdf.Content.Objects +{ + /// + /// The names of the op-codes. + /// + public enum OpCodeName + { + Dictionary, // Name followed by dictionary. + + // I know that this is not useable in VB or other languages with no case sensitivity. + + // Reference: TABLE A.1PDF content stream operators / Page 985 + + /// + /// Close, fill, and stroke path using nonzero winding number rule. + /// + b, + + /// + /// Fill and stroke path using nonzero winding number rule. + /// + B, + + /// + /// Close, fill, and stroke path using even-odd rule. + /// + bx, // b* + + /// + /// Fill and stroke path using even-odd rule. + /// + Bx, // B* + + /// + /// (PDF 1.2) Begin marked-content sequence with property list. + /// + BDC, + + /// + /// Begin inline image object. + /// + BI, + + /// + /// (PDF 1.2) Begin marked-content sequence. + /// + BMC, + + /// + /// Begin text object. + /// + BT, + + /// + /// (PDF 1.1) Begin compatibility section. + /// + BX, + + c, + cm, + CS, + cs, + d, + d0, + d1, + Do, + + /// + /// (PDF 1.2) Define marked-content point with property list. + /// + DP, + + EI, + + /// + /// (PDF 1.2) End marked-content sequence. + /// + EMC, + + ET, + + /// + /// (PDF 1.1) End compatibility section. + /// + EX, + + f, + F, + fx, // f* + G, + g, + gs, + h, + i, + ID, + j, + J, + K, + k, + l, + m, + M, + + /// + /// (PDF 1.2) Define marked-content point + /// + MP, + + n, + q, + Q, + re, + RG, + rg, + ri, + s, + S, + SC, + sc, + SCN, + scn, + sh, + Tx, // T* + Tc, + Td, + TD, + Tf, + Tj, + TJ, + TL, + Tm, + Tr, + Ts, + Tw, + Tz, + v, + w, + W, + Wx, // W* + y, + + /// + /// Move to next line and show text. + /// + QuoteSingle, // ' + + /// + /// Set word and character spacing, move to next line, and show text. + /// + QuoteDbl, // " + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/CLexer.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/CLexer.cs new file mode 100644 index 00000000..7e536db2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/CLexer.cs @@ -0,0 +1,847 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using System.Diagnostics; +using System.Text; +using System.IO; +using PdfSharp.Internal; + +#pragma warning disable 1591 + +namespace PdfSharp.Pdf.Content +{ + /// + /// Lexical analyzer for PDF content files. Adobe specifies no grammar, but it seems that it + /// is a simple post-fix notation. + /// + public class CLexer + { + /// + /// Initializes a new instance of the Lexer class. + /// + public CLexer(byte[] content) + { + _content = content; + _charIndex = 0; + } + + /// + /// Initializes a new instance of the Lexer class. + /// + public CLexer(MemoryStream content) + { + _content = content.ToArray(); + _charIndex = 0; + } + + /// + /// Reads the next token and returns its type. + /// + public CSymbol ScanNextToken() + { + Again: + ClearToken(); + char ch = MoveToNonWhiteSpace(); + switch (ch) + { + case '%': + // Eat comments, the parser doesn't handle them + //return symbol = ScanComment(); + ScanComment(); + goto Again; + + case '/': + return _symbol = ScanName(); + + //case 'R': + // if (Lexer.IsWhiteSpace(nextChar)) + // { + // ScanNextChar(); + // return Symbol.R; + // } + // break; + + case '+': + case '-': + return _symbol = ScanNumber(); + + case '[': + ScanNextChar(); + return _symbol = CSymbol.BeginArray; + + case ']': + ScanNextChar(); + return _symbol = CSymbol.EndArray; + + case '(': + return _symbol = ScanLiteralString(); + + case '<': + if (_nextChar == '<') + return _symbol = ScanDictionary(); + return _symbol = ScanHexadecimalString(); + + case '.': + return _symbol = ScanNumber(); + + case '"': + case '\'': + return _symbol = ScanOperator(); + } + if (char.IsDigit(ch)) + return _symbol = ScanNumber(); + + if (char.IsLetter(ch)) + return _symbol = ScanOperator(); + + if (ch == Chars.EOF) + return _symbol = CSymbol.Eof; + + ContentReaderDiagnostics.HandleUnexpectedCharacter(ch); + return _symbol = CSymbol.None; + } + + /// + /// Scans a comment line. (Not yet used, comments are skipped by lexer.) + /// + public CSymbol ScanComment() + { + Debug.Assert(_currChar == Chars.Percent); + + ClearToken(); + char ch; + while ((ch = AppendAndScanNextChar()) != Chars.LF && ch != Chars.EOF) { } + return _symbol = CSymbol.Comment; + } + + /// + /// Scans the bytes of an inline image. + /// NYI: Just scans over it. + /// + public CSymbol ScanInlineImage() + { + // TODO: Implement inline images. + // Skip this: + // BI + // … Key-value pairs … + // ID + // … Image data … + // EI + + bool ascii85 = false; + do + { + ScanNextToken(); + // HACK: Is image ASCII85 decoded? + if (!ascii85 && _symbol == CSymbol.Name && (Token == "/ASCII85Decode" || Token == "/A85")) + ascii85 = true; + } while (_symbol != CSymbol.Operator || Token != "ID"); + + if (ascii85) + { + // Look for '~>' because 'EI' may be part of the encoded image. + while (_currChar != Chars.EOF && (_currChar != '~' || _nextChar != '>')) + ScanNextChar(); + if (_currChar == Chars.EOF) + ContentReaderDiagnostics.HandleUnexpectedCharacter(_currChar); + } + + // Look for 'EI', as 'EI' may be part of the binary image data here too. + while (_currChar != Chars.EOF) + { + if (IsWhiteSpace(_currChar)) + { + if (ScanNextChar() == 'E') + if (ScanNextChar() == 'I') + if (IsWhiteSpace(ScanNextChar())) + break; + } + else + ScanNextChar(); + } + if (_currChar == Chars.EOF) + ContentReaderDiagnostics.HandleUnexpectedCharacter(_currChar); + + // We currently do nothing with inline images. + return CSymbol.None; + } + + /// + /// Scans a name. + /// + public CSymbol ScanName() + { + Debug.Assert(_currChar == Chars.Slash); + + ClearToken(); + while (true) + { + char ch = AppendAndScanNextChar(); + if (IsWhiteSpace(ch) || IsDelimiter(ch)) + return _symbol = CSymbol.Name; + + if (ch == '#') + { + ScanNextChar(); + char[] hex = new char[2]; + hex[0] = _currChar; + hex[1] = _nextChar; + ScanNextChar(); + // TODO Check syntax + ch = (char)(ushort)int.Parse(new string(hex), NumberStyles.AllowHexSpecifier); + _currChar = ch; + } + } + } + + protected CSymbol ScanDictionary() + { + // TODO Do an actual recursive parse instead of this simple scan. + + ClearToken(); + _token.Append(_currChar); // '<' + _token.Append(ScanNextChar()); // '<' + + bool inString = false, inHexString = false; + int nestedDict = 0, nestedStringParen = 0; + char ch; + while (true) + { + _token.Append(ch = ScanNextChar()); + if (ch == '<') + { + if (_nextChar == '<') + { + _token.Append(ScanNextChar()); + ++nestedDict; + } + else + inHexString = true; + } + else if (!inHexString && ch == '(') + { + if (inString) + ++nestedStringParen; + else + { + inString = true; + nestedStringParen = 0; + } + } + else if (inString && ch == ')') + { + if (nestedStringParen > 0) + --nestedStringParen; + else + inString = false; + } + else if (inString && ch == '\\') + _token.Append(ScanNextChar()); + else if (ch == '>') + { + if (inHexString) + inHexString = false; + else if (_nextChar == '>') + { + _token.Append(ScanNextChar()); + if (nestedDict > 0) + --nestedDict; + else + { + ScanNextChar(); + +#if true + return CSymbol.Dictionary; +#else + return CSymbol.String; +#endif + } + } + } + else if (ch == Chars.EOF) + ContentReaderDiagnostics.HandleUnexpectedCharacter(ch); + } + } + + /// + /// Scans an integer or real number. + /// + public CSymbol ScanNumber() + { + long value = 0; + int decimalDigits = 0; + bool period = false; + bool negative = false; + + ClearToken(); + char ch = _currChar; + if (ch == '+' || ch == '-') + { + if (ch == '-') + negative = true; + _token.Append(ch); + ch = ScanNextChar(); + } + while (true) + { + if (char.IsDigit(ch)) + { + _token.Append(ch); + if (decimalDigits < 10) + { + value = 10 * value + ch - '0'; + if (period) + decimalDigits++; + } + } + else if (ch == '.') + { + if (period) + ContentReaderDiagnostics.ThrowContentReaderException("More than one period in number."); + + period = true; + _token.Append(ch); + } + else + break; + ch = ScanNextChar(); + } + + if (negative) + value = -value; + if (period) + { + if (decimalDigits > 0) + { + _tokenAsReal = value / PowersOf10[decimalDigits]; + //_tokenAsLong = value / PowersOf10[decimalDigits]; + } + else + { + _tokenAsReal = value; + _tokenAsLong = value; + } + return CSymbol.Real; + } + _tokenAsLong = value; + _tokenAsReal = Convert.ToDouble(value); + + Debug.Assert(Int64.Parse(_token.ToString(), CultureInfo.InvariantCulture) == value); + + if (value >= Int32.MinValue && value < Int32.MaxValue) + return CSymbol.Integer; + + ContentReaderDiagnostics.ThrowNumberOutOfIntegerRange(value); + return CSymbol.Error; + } + static readonly double[] PowersOf10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000 }; + + /// + /// Scans an operator. + /// + public CSymbol ScanOperator() + { + ClearToken(); + char ch = _currChar; + // Scan token + while (IsOperatorChar(ch)) + ch = AppendAndScanNextChar(); + + return _symbol = CSymbol.Operator; + } + + // TODO + public CSymbol ScanLiteralString() + { + Debug.Assert(_currChar == Chars.ParenLeft); + + ClearToken(); + int parenLevel = 0; + char ch = ScanNextChar(); + // Test UNICODE string + if (ch == '\xFE' && _nextChar == '\xFF') + { + // I'm not sure if the code is correct in any case. + // ? Can a UNICODE character not start with ')' as hibyte + // ? What about \# escape sequences + ScanNextChar(); + char chHi = ScanNextChar(); + if (chHi == ')') + { + // The empty unicode string... + ScanNextChar(); + return _symbol = CSymbol.String; + } + char chLo = ScanNextChar(); + ch = (char)(chHi * 256 + chLo); + while (true) + { + SkipChar: + switch (ch) + { + case '(': + parenLevel++; + break; + + case ')': + if (parenLevel == 0) + { + ScanNextChar(); + return _symbol = CSymbol.String; + } + parenLevel--; + break; + + case '\\': + { + // TODO: not sure that this is correct... + ch = ScanNextChar(); + switch (ch) + { + case 'n': + ch = Chars.LF; + break; + + case 'r': + ch = Chars.CR; + break; + + case 't': + ch = Chars.HT; + break; + + case 'b': + ch = Chars.BS; + break; + + case 'f': + ch = Chars.FF; + break; + + case '(': + ch = Chars.ParenLeft; + break; + + case ')': + ch = Chars.ParenRight; + break; + + case '\\': + ch = Chars.BackSlash; + break; + + case Chars.LF: + ch = ScanNextChar(); + goto SkipChar; + + default: + if (char.IsDigit(ch)) + { + // Octal character code + int n = ch - '0'; + if (char.IsDigit(_nextChar)) + { + n = n * 8 + ScanNextChar() - '0'; + if (char.IsDigit(_nextChar)) + n = n * 8 + ScanNextChar() - '0'; + } + ch = (char)n; + } + break; + } + break; + } + + //case '#': + // ContentReaderDiagnostics.HandleUnexpectedCharacter('#'); + // break; + + default: + // Every other char is appended to the token. + break; + } + _token.Append(ch); + chHi = ScanNextChar(); + if (chHi == ')') + { + ScanNextChar(); + return _symbol = CSymbol.String; + } + chLo = ScanNextChar(); + ch = (char)(chHi * 256 + chLo); + } + } + else + { + // 8-bit characters + while (true) + { + SkipChar: + switch (ch) + { + case '(': + parenLevel++; + break; + + case ')': + if (parenLevel == 0) + { + ScanNextChar(); + return _symbol = CSymbol.String; + } + parenLevel--; + break; + + case '\\': + { + ch = ScanNextChar(); + switch (ch) + { + case 'n': + ch = Chars.LF; + break; + + case 'r': + ch = Chars.CR; + break; + + case 't': + ch = Chars.HT; + break; + + case 'b': + ch = Chars.BS; + break; + + case 'f': + ch = Chars.FF; + break; + + case '(': + ch = Chars.ParenLeft; + break; + + case ')': + ch = Chars.ParenRight; + break; + + case '\\': + ch = Chars.BackSlash; + break; + + case Chars.LF: + ch = ScanNextChar(); + goto SkipChar; + + default: + if (char.IsDigit(ch)) + { + // Octal character code. + int n = ch - '0'; + if (char.IsDigit(_nextChar)) + { + n = n * 8 + ScanNextChar() - '0'; + if (char.IsDigit(_nextChar)) + n = n * 8 + ScanNextChar() - '0'; + } + ch = (char)n; + } + break; + } + break; + } + + //case '#': + // ContentReaderDiagnostics.HandleUnexpectedCharacter('#'); + // break; + + default: + // Every other char is appended to the token. + break; + } + _token.Append(ch); + //token.Append(Encoding.GetEncoding(1252).GetString(new byte[] { (byte)ch })); + ch = ScanNextChar(); + } + } + } + + // TODO + public CSymbol ScanHexadecimalString() + { + Debug.Assert(_currChar == Chars.Less); + + ClearToken(); + char[] hex = new char[2]; + ScanNextChar(); + while (true) + { + MoveToNonWhiteSpace(); + if (_currChar == '>') + { + ScanNextChar(); + break; + } + if (char.IsLetterOrDigit(_currChar)) + { + hex[0] = char.ToUpper(_currChar); + hex[1] = char.ToUpper(_nextChar); + int ch = int.Parse(new string(hex), NumberStyles.AllowHexSpecifier); + _token.Append(Convert.ToChar(ch)); + ScanNextChar(); + ScanNextChar(); + } + } + string chars = _token.ToString(); + int count = chars.Length; + if (count > 2 && chars[0] == (char)0xFE && chars[1] == (char)0xFF) + { + Debug.Assert(count % 2 == 0); + _token.Length = 0; + for (int idx = 2; idx < count; idx += 2) + _token.Append((char)(chars[idx] * 256 + chars[idx + 1])); + } + return _symbol = CSymbol.HexString; + } + + /// + /// Move current position one character further in content stream. + /// + internal char ScanNextChar() + { + if (ContLength <= _charIndex) + { + _currChar = Chars.EOF; + if (IsOperatorChar(_nextChar)) + _token.Append(_nextChar); + _nextChar = Chars.EOF; + } + else + { + _currChar = _nextChar; + _nextChar = (char)_content[_charIndex++]; + if (_currChar == Chars.CR) + { + if (_nextChar == Chars.LF) + { + // Treat CR LF as LF + _currChar = _nextChar; + if (ContLength <= _charIndex) + _nextChar = Chars.EOF; + else + _nextChar = (char)_content[_charIndex++]; + } + else + { + // Treat single CR as LF + _currChar = Chars.LF; + } + } + } + return _currChar; + } + + /// + /// Resets the current token to the empty string. + /// + void ClearToken() + { + _token.Length = 0; + _tokenAsLong = 0; + _tokenAsReal = 0; + } + + /// + /// Appends current character to the token and reads next one. + /// + internal char AppendAndScanNextChar() + { + _token.Append(_currChar); + return ScanNextChar(); + } + + /// + /// If the current character is not a white space, the function immediately returns it. + /// Otherwise the PDF cursor is moved forward to the first non-white space or EOF. + /// White spaces are NUL, HT, LF, FF, CR, and SP. + /// + public char MoveToNonWhiteSpace() + { + while (_currChar != Chars.EOF) + { + switch (_currChar) + { + case Chars.NUL: + case Chars.HT: + case Chars.LF: + case Chars.FF: + case Chars.CR: + case Chars.SP: + ScanNextChar(); + break; + + default: + return _currChar; + } + } + return _currChar; + } + + /// + /// Gets or sets the current symbol. + /// + public CSymbol Symbol + { + get { return _symbol; } + set { _symbol = value; } + } + + /// + /// Gets the current token. + /// + public string Token + { + get { return _token.ToString(); } + } + + /// + /// Interprets current token as integer literal. + /// + internal int TokenToInteger + { + get + { + Debug.Assert(_tokenAsLong == int.Parse(_token.ToString(), CultureInfo.InvariantCulture)); + return (int)_tokenAsLong; + } + } + + /// + /// Interpret current token as real or integer literal. + /// + internal double TokenToReal + { + get + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + Debug.Assert(_tokenAsReal == double.Parse(_token.ToString(), CultureInfo.InvariantCulture)); + return _tokenAsReal; + } + } + + /// + /// Indicates whether the specified character is a content stream white-space character. + /// + internal static bool IsWhiteSpace(char ch) + { + switch (ch) + { + case Chars.NUL: // 0 Null + case Chars.HT: // 9 Tab + case Chars.LF: // 10 Line feed + case Chars.FF: // 12 Form feed + case Chars.CR: // 13 Carriage return + case Chars.SP: // 32 Space + return true; + } + return false; + } + + /// + /// Indicates whether the specified character is an content operator character. + /// + internal static bool IsOperatorChar(char ch) + { + if (char.IsLetter(ch)) + return true; + switch (ch) + { + case Chars.Asterisk: // * + case Chars.QuoteSingle: // ' + case Chars.QuoteDbl: // " + return true; + } + return false; + } + + /// + /// Indicates whether the specified character is a PDF delimiter character. + /// + internal static bool IsDelimiter(char ch) + { + switch (ch) + { + case '(': + case ')': + case '<': + case '>': + case '[': + case ']': + //case '{': + //case '}': + case '/': + case '%': + return true; + } + return false; + } + + /// + /// Gets the length of the content. + /// + public int ContLength + { + get { return _content.Length; } + } + + // ad + public int Position + { + get { return _charIndex; } + set + { + _charIndex = value; + _currChar = (char)_content[_charIndex - 1]; + _nextChar = (char)_content[_charIndex - 1]; + } + } + + readonly byte[] _content; + int _charIndex; + char _currChar; + char _nextChar; + + readonly StringBuilder _token = new StringBuilder(); + long _tokenAsLong; + double _tokenAsReal; + CSymbol _symbol = CSymbol.None; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/CParser.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/CParser.cs new file mode 100644 index 00000000..afbc2b0c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/CParser.cs @@ -0,0 +1,231 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.IO; +using PdfSharp.Internal; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Content.Objects; + +#pragma warning disable 1591 + +namespace PdfSharp.Pdf.Content +{ + /// + /// Provides the functionality to parse PDF content streams. + /// + public sealed class CParser + { + public CParser(PdfPage page) + { + _page = page; + PdfContent content = page.Contents.CreateSingleContent(); + byte[] bytes = content.Stream.Value; + _lexer = new CLexer(bytes); + } + + public CParser(byte[] content) + { + _lexer = new CLexer(content); + } + + public CParser(MemoryStream content) + { + _lexer = new CLexer(content.ToArray()); + } + + + public CParser(CLexer lexer) + { + _lexer = lexer; + } + + public CSymbol Symbol + { + get { return _lexer.Symbol; } + } + + public CSequence ReadContent() + { + CSequence sequence = new CSequence(); + ParseObject(sequence, CSymbol.Eof); + + return sequence; + } + + /// + /// Parses whatever comes until the specified stop symbol is reached. + /// + void ParseObject(CSequence sequence, CSymbol stop) + { + CSymbol symbol; + while ((symbol = ScanNextToken()) != CSymbol.Eof) + { + if (symbol == stop) + return; + + CString s; + COperator op; + switch (symbol) + { + case CSymbol.Comment: + // ignore comments + break; + + case CSymbol.Integer: + CInteger n = new CInteger(); + n.Value = _lexer.TokenToInteger; + _operands.Add(n); + break; + + case CSymbol.Real: + CReal r = new CReal(); + r.Value = _lexer.TokenToReal; + _operands.Add(r); + break; + + case CSymbol.String: + case CSymbol.HexString: + case CSymbol.UnicodeString: + case CSymbol.UnicodeHexString: + s = new CString(); + s.Value = _lexer.Token; + _operands.Add(s); + break; + + case CSymbol.Dictionary: + s = new CString(); + s.Value = _lexer.Token; + s.CStringType = CStringType.Dictionary; + _operands.Add(s); + op = CreateOperator(OpCodeName.Dictionary); + //_operands.Clear(); + sequence.Add(op); + + break; + + case CSymbol.Name: + CName name = new CName(); + name.Name = _lexer.Token; + _operands.Add(name); + break; + + case CSymbol.Operator: + op = CreateOperator(); + //_operands.Clear(); + sequence.Add(op); + break; + + case CSymbol.BeginArray: + CArray array = new CArray(); + if (_operands.Count != 0) + ContentReaderDiagnostics.ThrowContentReaderException("Array within array..."); + + ParseObject(array, CSymbol.EndArray); + array.Add(_operands); + _operands.Clear(); + _operands.Add((CObject)array); + break; + + case CSymbol.EndArray: + ContentReaderDiagnostics.HandleUnexpectedCharacter(']'); + break; + +#if DEBUG + default: + Debug.Assert(false); + break; +#endif + } + } + } + + COperator CreateOperator() + { + string name = _lexer.Token; + COperator op = OpCodes.OperatorFromName(name); + return CreateOperator(op); + } + + COperator CreateOperator(OpCodeName nameop) + { + string name = nameop.ToString(); + COperator op = OpCodes.OperatorFromName(name); + return CreateOperator(op); + } + + COperator CreateOperator(COperator op) + { + if (op.OpCode.OpCodeName == OpCodeName.BI) + { + _lexer.ScanInlineImage(); + } +#if DEBUG + if (op.OpCode.Operands != -1 && op.OpCode.Operands != _operands.Count) + { + if (op.OpCode.OpCodeName != OpCodeName.ID) + { + GetType(); + Debug.Assert(false, "Invalid number of operands."); + } + } +#endif + op.Operands.Add(_operands); + _operands.Clear(); + return op; + } + + CSymbol ScanNextToken() + { + return _lexer.ScanNextToken(); + } + + CSymbol ScanNextToken(out string token) + { + CSymbol symbol = _lexer.ScanNextToken(); + token = _lexer.Token; + return symbol; + } + + /// + /// Reads the next symbol that must be the specified one. + /// + CSymbol ReadSymbol(CSymbol symbol) + { + CSymbol current = _lexer.ScanNextToken(); + if (symbol != current) + ContentReaderDiagnostics.ThrowContentReaderException(PSSR.UnexpectedToken(_lexer.Token)); + return current; + } + + readonly CSequence _operands = new CSequence(); + PdfPage _page; + readonly CLexer _lexer; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/Chars.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/Chars.cs new file mode 100644 index 00000000..b480c55d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/Chars.cs @@ -0,0 +1,80 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Pdf.Content +{ + /// + /// Character table by name. Same as PdfSharp.Pdf.IO.Chars. Not yet clear if necessary. + /// + internal static class Chars + { + public const char EOF = (char)65535; //unchecked((char)(-1)); + public const char NUL = '\0'; // EOF + public const char CR = '\x0D'; // ignored by lexer + public const char LF = '\x0A'; // Line feed + public const char BEL = '\a'; // Bell + public const char BS = '\b'; // Backspace + public const char FF = '\f'; // Form feed + public const char HT = '\t'; // Horizontal tab + public const char VT = '\v'; // Vertical tab + public const char NonBreakableSpace = (char)160; // char(160) + + // The following names come from "PDF Reference Third Edition" + // Appendix D.1, Latin Character Set and Encoding + public const char SP = ' '; + public const char QuoteDbl = '"'; + public const char QuoteSingle = '\''; + public const char ParenLeft = '('; + public const char ParenRight = ')'; + public const char BraceLeft = '{'; + public const char BraceRight = '}'; + public const char BracketLeft = '['; + public const char BracketRight = ']'; + public const char Less = '<'; + public const char Greater = '>'; + public const char Equal = '='; + public const char Period = '.'; + public const char Semicolon = ';'; + public const char Colon = ':'; + public const char Slash = '/'; + public const char Bar = '|'; + public const char BackSlash = '\\'; + public const char Percent = '%'; + public const char Dollar = '$'; + public const char At = '@'; + public const char NumberSign = '#'; + public const char Asterisk = '*'; + public const char Question = '?'; + public const char Hyphen = '-'; // char(45) + public const char SoftHyphen = '­'; // char(173) + public const char Currency = '¤'; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReader.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReader.cs new file mode 100644 index 00000000..016e28ad --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReader.cs @@ -0,0 +1,74 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.IO; +using PdfSharp.Pdf.Content.Objects; + +namespace PdfSharp.Pdf.Content +{ + /// + /// Represents the functionality for reading PDF content streams. + /// + public static class ContentReader + { + /// + /// Reads the content stream(s) of the specified page. + /// + /// The page. + static public CSequence ReadContent(PdfPage page) + { + CParser parser = new CParser(page); + CSequence sequence = parser.ReadContent(); + + return sequence; + } + + /// + /// Reads the specified content. + /// + /// The content. + static public CSequence ReadContent(byte[] content) + { + CParser parser = new CParser(content); + CSequence sequence = parser.ReadContent(); + return sequence; + } + + /// + /// Reads the specified content. + /// + /// The content. + static public CSequence ReadContent(MemoryStream content) + { + CParser parser = new CParser(content); + CSequence sequence = parser.ReadContent(); + return sequence; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReaderException.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReaderException.cs new file mode 100644 index 00000000..52a7c1b8 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentReaderException.cs @@ -0,0 +1,62 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Content +{ + /// + /// Exception thrown by ContentReader. + /// + public class ContentReaderException : PdfSharpException + { + /// + /// Initializes a new instance of the class. + /// + public ContentReaderException() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public ContentReaderException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public ContentReaderException(string message, Exception innerException) : + base(message, innerException) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentWriter.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentWriter.cs new file mode 100644 index 00000000..73cff8af --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/ContentWriter.cs @@ -0,0 +1,231 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Content +{ + /// + /// Represents a writer for generation of PDF streams. + /// + internal class ContentWriter + { + public ContentWriter(Stream contentStream) + { + _stream = contentStream; +#if DEBUG + //layout = PdfWriterLayout.Verbose; +#endif + } + + public void Close(bool closeUnderlyingStream) + { + if (_stream != null && closeUnderlyingStream) + { +#if UWP + _stream.Dispose(); +#else + _stream.Close(); +#endif + _stream = null; + } + } + + public void Close() + { + Close(true); + } + + public int Position + { + get { return (int)_stream.Position; } + } + + //public PdfWriterLayout Layout + //{ + // get { return layout; } + // set { layout = value; } + //} + //PdfWriterLayout layout; + + //public PdfWriterOptions Options + //{ + // get { return options; } + // set { options = value; } + //} + //PdfWriterOptions options; + + // ----------------------------------------------------------- + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(bool value) + { + //WriteSeparator(CharCat.Character); + //WriteRaw(value ? bool.TrueString : bool.FalseString); + //lastCat = CharCat.Character; + } + + public void WriteRaw(string rawString) + { + if (String.IsNullOrEmpty(rawString)) + return; + //AppendBlank(rawString[0]); + byte[] bytes = PdfEncoders.RawEncoding.GetBytes(rawString); + _stream.Write(bytes, 0, bytes.Length); + _lastCat = GetCategory((char)bytes[bytes.Length - 1]); + } + + public void WriteLineRaw(string rawString) + { + if (String.IsNullOrEmpty(rawString)) + return; + //AppendBlank(rawString[0]); + byte[] bytes = PdfEncoders.RawEncoding.GetBytes(rawString); + _stream.Write(bytes, 0, bytes.Length); + _stream.Write(new byte[] { (byte)'\n' }, 0, 1); + _lastCat = GetCategory((char)bytes[bytes.Length - 1]); + } + + public void WriteRaw(char ch) + { + Debug.Assert(ch < 256, "Raw character greater than 255 detected."); + _stream.WriteByte((byte)ch); + _lastCat = GetCategory(ch); + } + + /// + /// Gets or sets the indentation for a new indentation level. + /// + internal int Indent + { + get { return _indent; } + set { _indent = value; } + } + protected int _indent = 2; + protected int _writeIndent = 0; + + /// + /// Increases indent level. + /// + void IncreaseIndent() + { + _writeIndent += _indent; + } + + /// + /// Decreases indent level. + /// + void DecreaseIndent() + { + _writeIndent -= _indent; + } + + /// + /// Gets an indent string of current indent. + /// + string IndentBlanks + { + get { return new string(' ', _writeIndent); } + } + + void WriteIndent() + { + WriteRaw(IndentBlanks); + } + + void WriteSeparator(CharCat cat, char ch) + { + switch (_lastCat) + { + //case CharCat.NewLine: + // if (this.layout == PdfWriterLayout.Verbose) + // WriteIndent(); + // break; + + case CharCat.Delimiter: + break; + + //case CharCat.Character: + // if (this.layout == PdfWriterLayout.Verbose) + // { + // //if (cat == CharCat.Character || ch == '/') + // this.stream.WriteByte((byte)' '); + // } + // else + // { + // if (cat == CharCat.Character) + // this.stream.WriteByte((byte)' '); + // } + // break; + } + } + + void WriteSeparator(CharCat cat) + { + WriteSeparator(cat, '\0'); + } + + public void NewLine() + { + if (_lastCat != CharCat.NewLine) + WriteRaw('\n'); + } + + CharCat GetCategory(char ch) + { + //if (Lexer.IsDelimiter(ch)) + // return CharCat.Delimiter; + //if (ch == Chars.LF) + // return CharCat.NewLine; + return CharCat.Character; + } + + enum CharCat + { + NewLine, + Character, + Delimiter, + } + CharCat _lastCat; + + /// + /// Gets the underlying stream. + /// + internal Stream Stream + { + get { return _stream; } + } + Stream _stream; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Content/enums/Symbol.cs b/src/PDFsharp/src/PdfSharp/Pdf.Content/enums/Symbol.cs new file mode 100644 index 00000000..49288e1d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Content/enums/Symbol.cs @@ -0,0 +1,55 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Content +{ + /// + /// Terminal symbols recognized by PDF content stream lexer. + /// + public enum CSymbol + { +#pragma warning disable 1591 + None, + Comment, + Integer, + Real, + /*Boolean?,*/ + String, + HexString, + UnicodeString, + UnicodeHexString, + Name, + Operator, + BeginArray, + EndArray, + Dictionary, // HACK: << ... >> is scanned as string literal. + Eof, + Error = -1, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/Ascii85Decode.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Ascii85Decode.cs new file mode 100644 index 00000000..258e17c9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Ascii85Decode.cs @@ -0,0 +1,285 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Implements the ASCII85Decode filter. + /// + public class Ascii85Decode : Filter + { + // Reference: 3.3.2 ASCII85Decode Filter / Page 69 + + /// + /// Encodes the specified data. + /// + public override byte[] Encode(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + int length = data.Length; // length == 0 is must not be treated as a special case. + int words = length / 4; + int rest = length - (words * 4); + byte[] result = new byte[words * 5 + (rest == 0 ? 0 : rest + 1) + 2]; + + int idxIn = 0, idxOut = 0; + int wCount = 0; + while (wCount < words) + { + uint val = ((uint)data[idxIn++] << 24) + ((uint)data[idxIn++] << 16) + ((uint)data[idxIn++] << 8) + data[idxIn++]; + if (val == 0) + { + result[idxOut++] = (byte)'z'; + } + else + { + byte c5 = (byte)(val % 85 + '!'); + val /= 85; + byte c4 = (byte)(val % 85 + '!'); + val /= 85; + byte c3 = (byte)(val % 85 + '!'); + val /= 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + + result[idxOut++] = c1; + result[idxOut++] = c2; + result[idxOut++] = c3; + result[idxOut++] = c4; + result[idxOut++] = c5; + } + wCount++; + } + if (rest == 1) + { + uint val = (uint)data[idxIn] << 24; + val /= 85 * 85 * 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + + result[idxOut++] = c1; + result[idxOut++] = c2; + } + else if (rest == 2) + { + uint val = ((uint)data[idxIn++] << 24) + ((uint)data[idxIn] << 16); + val /= 85 * 85; + byte c3 = (byte)(val % 85 + '!'); + val /= 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + + result[idxOut++] = c1; + result[idxOut++] = c2; + result[idxOut++] = c3; + } + else if (rest == 3) + { + uint val = ((uint)data[idxIn++] << 24) + ((uint)data[idxIn++] << 16) + ((uint)data[idxIn] << 8); + val /= 85; + byte c4 = (byte)(val % 85 + '!'); + val /= 85; + byte c3 = (byte)(val % 85 + '!'); + val /= 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + + result[idxOut++] = c1; + result[idxOut++] = c2; + result[idxOut++] = c3; + result[idxOut++] = c4; + } + result[idxOut++] = (byte)'~'; + result[idxOut++] = (byte)'>'; + + if (idxOut < result.Length) + Array.Resize(ref result, idxOut); + + return result; + } + + /// + /// Decodes the specified data. + /// + public override byte[] Decode(byte[] data, FilterParms parms) + { + if (data == null) + throw new ArgumentNullException("data"); + + int idx; + int length = data.Length; + int zCount = 0; + int idxOut = 0; + for (idx = 0; idx < length; idx++) + { + char ch = (char)data[idx]; + if (ch >= '!' && ch <= 'u') + data[idxOut++] = (byte)ch; + else if (ch == 'z') + { + data[idxOut++] = (byte)ch; + zCount++; + } + else if (ch == '~') + { + if ((char)data[idx + 1] != '>') + throw new ArgumentException("Illegal character.", "data"); + break; + } + // ingnore unknown character + } + // Loop not ended with break? + if (idx == length) + throw new ArgumentException("Illegal character.", "data"); + + length = idxOut; + int nonZero = length - zCount; + int byteCount = 4 * (zCount + (nonZero / 5)); // full 4 byte blocks + + int remainder = nonZero % 5; + if (remainder == 1) + throw new InvalidOperationException("Illegal character."); + + if (remainder != 0) + byteCount += remainder - 1; + + byte[] output = new byte[byteCount]; + + idxOut = 0; + idx = 0; + while (idx + 4 < length) + { + char ch = (char)data[idx]; + if (ch == 'z') + { + idx++; + idxOut += 4; + } + else + { + // TODO: check + long value = + (long)(data[idx++] - '!') * (85 * 85 * 85 * 85) + + (uint)(data[idx++] - '!') * (85 * 85 * 85) + + (uint)(data[idx++] - '!') * (85 * 85) + + (uint)(data[idx++] - '!') * 85 + + (uint)(data[idx++] - '!'); + + if (value > UInt32.MaxValue) + throw new InvalidOperationException("Value of group greater than 2 power 32 - 1."); + + output[idxOut++] = (byte)(value >> 24); + output[idxOut++] = (byte)(value >> 16); + output[idxOut++] = (byte)(value >> 8); + output[idxOut++] = (byte)value; + } + } + + // I have found no appropriate algorithm, so I write my own. In some rare cases the value must not + // increased by one, but I cannot found a general formula or a proof. + // All possible cases are tested programmatically. + if (remainder == 2) // one byte + { + uint value = + (uint)(data[idx++] - '!') * (85 * 85 * 85 * 85) + + (uint)(data[idx] - '!') * (85 * 85 * 85); + + // Always increase if not zero (tried out). + if (value != 0) + value += 0x01000000; + + output[idxOut] = (byte)(value >> 24); + } + else if (remainder == 3) // two bytes + { + int idxIn = idx; + uint value = + (uint)(data[idx++] - '!') * (85 * 85 * 85 * 85) + + (uint)(data[idx++] - '!') * (85 * 85 * 85) + + (uint)(data[idx] - '!') * (85 * 85); + + if (value != 0) + { + value &= 0xFFFF0000; + uint val = value / (85 * 85); + byte c3 = (byte)(val % 85 + '!'); + val /= 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + if (c1 != data[idxIn] || c2 != data[idxIn + 1] || c3 != data[idxIn + 2]) + { + value += 0x00010000; + //Count2++; + } + } + output[idxOut++] = (byte)(value >> 24); + output[idxOut] = (byte)(value >> 16); + } + else if (remainder == 4) // three bytes + { + int idxIn = idx; + uint value = + (uint)(data[idx++] - '!') * (85 * 85 * 85 * 85) + + (uint)(data[idx++] - '!') * (85 * 85 * 85) + + (uint)(data[idx++] - '!') * (85 * 85) + + (uint)(data[idx] - '!') * 85; + + if (value != 0) + { + value &= 0xFFFFFF00; + uint val = value / 85; + byte c4 = (byte)(val % 85 + '!'); + val /= 85; + byte c3 = (byte)(val % 85 + '!'); + val /= 85; + byte c2 = (byte)(val % 85 + '!'); + val /= 85; + byte c1 = (byte)(val + '!'); + if (c1 != data[idxIn] || c2 != data[idxIn + 1] || c3 != data[idxIn + 2] || c4 != data[idxIn + 3]) + { + value += 0x00000100; + //Count3++; + } + } + output[idxOut++] = (byte)(value >> 24); + output[idxOut++] = (byte)(value >> 16); + output[idxOut] = (byte)(value >> 8); + } + return output; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/AsciiHexDecode.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/AsciiHexDecode.cs new file mode 100644 index 00000000..f64fc0ce --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/AsciiHexDecode.cs @@ -0,0 +1,98 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Implements the ASCIIHexDecode filter. + /// + public class AsciiHexDecode : Filter + { + // Reference: 3.3.1 ASCIIHexDecode Filter / Page 69 + + /// + /// Encodes the specified data. + /// + public override byte[] Encode(byte[] data) + { + if (data == null) + throw new ArgumentNullException("data"); + + int count = data.Length; + byte[] bytes = new byte[2 * count]; + for (int i = 0, j = 0; i < count; i++) + { + byte b = data[i]; + bytes[j++] = (byte)((b >> 4) + ((b >> 4) < 10 ? (byte)'0' : (byte)('A' - 10))); + bytes[j++] = (byte)((b & 0xF) + ((b & 0xF) < 10 ? (byte)'0' : (byte)('A' - 10))); + } + return bytes; + } + + /// + /// Decodes the specified data. + /// + public override byte[] Decode(byte[] data, FilterParms parms) + { + if (data == null) + throw new ArgumentNullException("data"); + + data = RemoveWhiteSpace(data); + int count = data.Length; + // Ignore EOD (end of data) character. + // EOD can be anywhere in the stream, but makes sense only at the end of the stream. + if (count > 0 && data[count - 1] == '>') + --count; + if (count % 2 == 1) + { + count++; + byte[] temp = data; + data = new byte[count]; + temp.CopyTo(data, 0); + } + count >>= 1; + byte[] bytes = new byte[count]; + for (int i = 0, j = 0; i < count; i++) + { + // Must support 0-9, A-F, a-f - "Any other characters cause an error." + byte hi = data[j++]; + byte lo = data[j++]; + if (hi >= 'a' && hi <= 'f') + hi -= 32; + if (lo >= 'a' && lo <= 'f') + lo -= 32; + // TODO Throw on invalid characters. Stop when encountering EOD. Add one more byte if EOD is the lo byte. + bytes[i] = (byte)((hi > '9' ? hi - '7'/*'A' + 10*/: hi - '0') * 16 + (lo > '9' ? lo - '7'/*'A' + 10*/: lo - '0')); + } + return bytes; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filter.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filter.cs new file mode 100644 index 00000000..f737f8e1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filter.cs @@ -0,0 +1,130 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Reserved for future extension. + /// + public class FilterParms + { + // not yet used + } + + /// + /// Base class for all stream filters + /// + public abstract class Filter + { + /// + /// When implemented in a derived class encodes the specified data. + /// + public abstract byte[] Encode(byte[] data); + + /// + /// Encodes a raw string. + /// + public virtual byte[] Encode(string rawString) + { + byte[] bytes = PdfEncoders.RawEncoding.GetBytes(rawString); + bytes = Encode(bytes); + return bytes; + } + + /// + /// When implemented in a derived class decodes the specified data. + /// + public abstract byte[] Decode(byte[] data, FilterParms parms); + + /// + /// Decodes the specified data. + /// + public byte[] Decode(byte[] data) + { + return Decode(data, null); + } + + /// + /// Decodes to a raw string. + /// + public virtual string DecodeToString(byte[] data, FilterParms parms) + { + byte[] bytes = Decode(data, parms); + string text = PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); + return text; + } + + /// + /// Decodes to a raw string. + /// + public string DecodeToString(byte[] data) + { + return DecodeToString(data, null); + } + + /// + /// Removes all white spaces from the data. The function assumes that the bytes are characters. + /// + protected byte[] RemoveWhiteSpace(byte[] data) + { + int count = data.Length; + int j = 0; + for (int i = 0; i < count; i++, j++) + { + switch (data[i]) + { + case (byte)Chars.NUL: // 0 Null + case (byte)Chars.HT: // 9 Tab + case (byte)Chars.LF: // 10 Line feed + case (byte)Chars.FF: // 12 Form feed + case (byte)Chars.CR: // 13 Carriage return + case (byte)Chars.SP: // 32 Space + j--; + break; + + default: + if (i != j) + data[j] = data[i]; + break; + } + } + if (j < count) + { + byte[] temp = data; + data = new byte[j]; + for (int idx = 0; idx < j; idx++) + data[idx] = temp[idx]; + } + return data; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filtering.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filtering.cs new file mode 100644 index 00000000..0ee26a62 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/Filtering.cs @@ -0,0 +1,242 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Applies standard filters to streams. + /// + public static class Filtering + { + /// + /// Gets the filter specified by the case sensitive name. + /// + public static Filter GetFilter(string filterName) + { + if (filterName.StartsWith("/")) + filterName = filterName.Substring(1); + + // Some tools use abbreviations + switch (filterName) + { + case "ASCIIHexDecode": + case "AHx": + return _asciiHexDecode ?? (_asciiHexDecode = new AsciiHexDecode()); + + case "ASCII85Decode": + case "A85": + return _ascii85Decode ?? (_ascii85Decode = new Ascii85Decode()); + + case "LZWDecode": + case "LZW": + return _lzwDecode ?? (_lzwDecode = new LzwDecode()); + + case "FlateDecode": + case "Fl": + return _flateDecode ?? (_flateDecode = new FlateDecode()); + + //case "RunLengthDecode": + // if (RunLengthDecode == null) + // RunLengthDecode = new RunLengthDecode(); + // return RunLengthDecode; + // + //case "CCITTFaxDecode": + // if (CCITTFaxDecode == null) + // CCITTFaxDecode = new CCITTFaxDecode(); + // return CCITTFaxDecode; + // + //case "JBIG2Decode": + // if (JBIG2Decode == null) + // JBIG2Decode = new JBIG2Decode(); + // return JBIG2Decode; + // + //case "DCTDecode": + // if (DCTDecode == null) + // DCTDecode = new DCTDecode(); + // return DCTDecode; + // + //case "JPXDecode": + // if (JPXDecode == null) + // JPXDecode = new JPXDecode(); + // return JPXDecode; + // + //case "Crypt": + // if (Crypt == null) + // Crypt = new Crypt(); + // return Crypt; + + case "RunLengthDecode": + case "CCITTFaxDecode": + case "JBIG2Decode": + case "DCTDecode": + case "JPXDecode": + case "Crypt": + Debug.WriteLine("Filter not implemented: " + filterName); + return null; + } + throw new NotImplementedException("Unknown filter: " + filterName); + } + + /// + /// Gets the filter singleton. + /// + // ReSharper disable InconsistentNaming + public static AsciiHexDecode ASCIIHexDecode + // ReSharper restore InconsistentNaming + { + get { return _asciiHexDecode ?? (_asciiHexDecode = new AsciiHexDecode()); } + } + static AsciiHexDecode _asciiHexDecode; + + /// + /// Gets the filter singleton. + /// + public static Ascii85Decode ASCII85Decode + { + get { return _ascii85Decode ?? (_ascii85Decode = new Ascii85Decode()); } + } + static Ascii85Decode _ascii85Decode; + + /// + /// Gets the filter singleton. + /// + public static LzwDecode LzwDecode + { + get { return _lzwDecode ?? (_lzwDecode = new LzwDecode()); } + } + static LzwDecode _lzwDecode; + + /// + /// Gets the filter singleton. + /// + public static FlateDecode FlateDecode + { + get { return _flateDecode ?? (_flateDecode = new FlateDecode()); } + } + static FlateDecode _flateDecode; + + //runLengthDecode + //ccittFaxDecode + //jbig2Decode + //dctDecode + //jpxDecode + //crypt + + /// + /// Encodes the data with the specified filter. + /// + public static byte[] Encode(byte[] data, string filterName) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.Encode(data); + return null; + } + + /// + /// Encodes a raw string with the specified filter. + /// + public static byte[] Encode(string rawString, string filterName) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.Encode(rawString); + return null; + } + + /// + /// Decodes the data with the specified filter. + /// + public static byte[] Decode(byte[] data, string filterName, FilterParms parms) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.Decode(data, parms); + return null; + } + + /// + /// Decodes the data with the specified filter. + /// + public static byte[] Decode(byte[] data, string filterName) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.Decode(data, null); + return null; + } + + /// + /// Decodes the data with the specified filter. + /// + public static byte[] Decode(byte[] data, PdfItem filterItem) + { + byte[] result = null; + if (filterItem is PdfName) + { + Filter filter = GetFilter(filterItem.ToString()); + if (filter != null) + result = filter.Decode(data); + } + else if (filterItem is PdfArray) + { + PdfArray array = (PdfArray)filterItem; + foreach (PdfItem item in array) + data = Decode(data, item); + result = data; + } + return result; + } + + /// + /// Decodes to a raw string with the specified filter. + /// + public static string DecodeToString(byte[] data, string filterName, FilterParms parms) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.DecodeToString(data, parms); + return null; + } + + /// + /// Decodes to a raw string with the specified filter. + /// + public static string DecodeToString(byte[] data, string filterName) + { + Filter filter = GetFilter(filterName); + if (filter != null) + return filter.DecodeToString(data, null); + return null; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/FlateDecode.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/FlateDecode.cs new file mode 100644 index 00000000..6659ea40 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/FlateDecode.cs @@ -0,0 +1,220 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.IO; +using PdfSharp.Internal; +#if NET_ZIP +using System.IO.Compression; +#else +using PdfSharp.SharpZipLib.Zip.Compression; +using PdfSharp.SharpZipLib.Zip.Compression.Streams; +#endif + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Implements the FlateDecode filter by wrapping SharpZipLib. + /// + public class FlateDecode : Filter + { + // Reference: 3.3.3 LZWDecode and FlateDecode Filters / Page 71 + + /// + /// Encodes the specified data. + /// + public override byte[] Encode(byte[] data) + { + return Encode(data, PdfFlateEncodeMode.Default); + } + + /// + /// Encodes the specified data. + /// + public byte[] Encode(byte[] data, PdfFlateEncodeMode mode) + { + MemoryStream ms = new MemoryStream(); + + // DeflateStream/GZipStream does not work immediately and I have not the leisure to work it out. + // So I keep on using SharpZipLib even with .NET 2.0. +#if NET_ZIP + // See http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=97064 + // + // Excerpt from the RFC 1950 specs for first byte: + // + // CMF (Compression Method and flags) + // This byte is divided into a 4-bit compression method and a 4- + // bit information field depending on the compression method. + // + // bits 0 to 3 CM Compression method + // bits 4 to 7 CINFO Compression info + // + // CM (Compression method) + // This identifies the compression method used in the file. CM = 8 + // denotes the "deflate" compression method with a window size up + // to 32K. This is the method used by gzip and PNG (see + // references [1] and [2] in Chapter 3, below, for the reference + // documents). CM = 15 is reserved. It might be used in a future + // version of this specification to indicate the presence of an + // extra field before the compressed data. + // + // CINFO (Compression info) + // For CM = 8, CINFO is the base-2 logarithm of the LZ77 window + // size, minus eight (CINFO=7 indicates a 32K window size). Values + // of CINFO above 7 are not allowed in this version of the + // specification. CINFO is not defined in this specification for + // CM not equal to 8. + ms.WriteByte(0x78); + + // Excerpt from the RFC 1950 specs for second byte: + // + // FLG (FLaGs) + // This flag byte is divided as follows: + // + // bits 0 to 4 FCHECK (check bits for CMF and FLG) + // bit 5 FDICT (preset dictionary) + // bits 6 to 7 FLEVEL (compression level) + // + // The FCHECK value must be such that CMF and FLG, when viewed as + // a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG), + // is a multiple of 31. + // + // FDICT (Preset dictionary) + // If FDICT is set, a DICT dictionary identifier is present + // immediately after the FLG byte. The dictionary is a sequence of + // bytes which are initially fed to the compressor without + // producing any compressed output. DICT is the Adler-32 checksum + // of this sequence of bytes (see the definition of ADLER32 + // below). The decompressor can use this identifier to determine + // which dictionary has been used by the compressor. + // + // FLEVEL (Compression level) + // These flags are available for use by specific compression + // methods. The "deflate" method (CM = 8) sets these flags as + // follows: + // + // 0 - compressor used fastest algorithm + // 1 - compressor used fast algorithm + // 2 - compressor used default algorithm + // 3 - compressor used maximum compression, slowest algorithm + // + // The information in FLEVEL is not needed for decompression; it + // is there to indicate if recompression might be worthwhile. + ms.WriteByte(0x49); + + DeflateStream zip = new DeflateStream(ms, CompressionMode.Compress, true); + zip.Write(data, 0, data.Length); + zip.Close(); +#else + int level = Deflater.DEFAULT_COMPRESSION; + switch (mode) + { + case PdfFlateEncodeMode.BestCompression: + level = Deflater.BEST_COMPRESSION; + break; + case PdfFlateEncodeMode.BestSpeed: + level = Deflater.BEST_SPEED; + break; + } + DeflaterOutputStream zip = new DeflaterOutputStream(ms, new Deflater(level, false)); + zip.Write(data, 0, data.Length); + zip.Finish(); +#endif +#if !NETFX_CORE && !UWP + ms.Capacity = (int)ms.Length; + return ms.GetBuffer(); +#else + return ms.ToArray(); +#endif + } + + /// + /// Decodes the specified data. + /// + public override byte[] Decode(byte[] data, FilterParms parms) + { + MemoryStream msInput = new MemoryStream(data); + MemoryStream msOutput = new MemoryStream(); +#if NET_ZIP + // See http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=97064 + // It seems to work when skipping the first two bytes. + byte header; // 0x30 0x59 + header = (byte)msInput.ReadByte(); + //Debug.Assert(header == 48); + header = (byte)msInput.ReadByte(); + //Debug.Assert(header == 89); + DeflateStream zip = new DeflateStream(msInput, CompressionMode.Decompress, true); + int cbRead; + byte[] abResult = new byte[1024]; + do + { + cbRead = zip.Read(abResult, 0, abResult.Length); + if (cbRead > 0) + msOutput.Write(abResult, 0, cbRead); + } + while (cbRead > 0); + zip.Close(); + msOutput.Flush(); + if (msOutput.Length >= 0) + { + msOutput.Capacity = (int)msOutput.Length; + return msOutput.GetBuffer(); + } + return null; +#else + InflaterInputStream iis = new InflaterInputStream(msInput, new Inflater(false)); + int cbRead; + byte[] abResult = new byte[32768]; + do + { + cbRead = iis.Read(abResult, 0, abResult.Length); + if (cbRead > 0) + msOutput.Write(abResult, 0, cbRead); + } + while (cbRead > 0); +#if UWP + iis.Dispose(); +#else + iis.Close(); +#endif + msOutput.Flush(); + if (msOutput.Length >= 0) + { +#if NETFX_CORE || UWP || DNC10 + return msOutput.ToArray(); +#else + msOutput.Capacity = (int)msOutput.Length; + return msOutput.GetBuffer(); +#endif + } + return null; +#endif + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Filters/LzwDecode.cs b/src/PDFsharp/src/PdfSharp/Pdf.Filters/LzwDecode.cs new file mode 100644 index 00000000..c2d41446 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Filters/LzwDecode.cs @@ -0,0 +1,189 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// David Stephensen +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.IO; + +namespace PdfSharp.Pdf.Filters +{ + /// + /// Implements the LzwDecode filter. + /// + public class LzwDecode : Filter + { + // Reference: 3.3.3 LZWDecode and FlateDecode Filters / Page 71 + + /// + /// Throws a NotImplementedException because the obsolete LZW encoding is not supported by PDFsharp. + /// + public override byte[] Encode(byte[] data) + { + throw new NotImplementedException("PDFsharp does not support LZW encoding."); + } + + /// + /// Decodes the specified data. + /// + public override byte[] Decode(byte[] data, FilterParms parms) + { + if (data[0] == 0x00 && data[1] == 0x01) + throw new Exception("LZW flavour not supported."); + + MemoryStream outputStream = new MemoryStream(); + + InitializeDictionary(); + + _data = data; + _bytePointer = 0; + _nextData = 0; + _nextBits = 0; + int code, oldCode = 0; + byte[] str; + + while ((code = NextCode) != 257) + { + if (code == 256) + { + InitializeDictionary(); + code = NextCode; + if (code == 257) + { + break; + } + outputStream.Write(_stringTable[code], 0, _stringTable[code].Length); + oldCode = code; + + } + else + { + if (code < _tableIndex) + { + str = _stringTable[code]; + outputStream.Write(str, 0, str.Length); + AddEntry(_stringTable[oldCode], str[0]); + oldCode = code; + } + else + { + str = _stringTable[oldCode]; + outputStream.Write(str, 0, str.Length); + AddEntry(str, str[0]); + oldCode = code; + } + } + } + + if (outputStream.Length >= 0) + { +#if !NETFX_CORE && !UWP + outputStream.Capacity = (int)outputStream.Length; + return outputStream.GetBuffer(); +#else + return outputStream.ToArray(); +#endif + } + return null; + } + + /// + /// Initialize the dictionary. + /// + void InitializeDictionary() + { + _stringTable = new byte[8192][]; + + for (int i = 0; i < 256; i++) + { + _stringTable[i] = new byte[1]; + _stringTable[i][0] = (byte)i; + } + + _tableIndex = 258; + _bitsToGet = 9; + } + + /// + /// Add a new entry to the Dictionary. + /// + void AddEntry(byte[] oldstring, byte newstring) + { + int length = oldstring.Length; + byte[] str = new byte[length + 1]; + Array.Copy(oldstring, 0, str, 0, length); + str[length] = newstring; + + _stringTable[_tableIndex++] = str; + + if (_tableIndex == 511) + _bitsToGet = 10; + else if (_tableIndex == 1023) + _bitsToGet = 11; + else if (_tableIndex == 2047) + _bitsToGet = 12; + } + + /// + /// Returns the next set of bits. + /// + int NextCode + { + get + { + try + { + _nextData = (_nextData << 8) | (_data[_bytePointer++] & 0xff); + _nextBits += 8; + + if (_nextBits < _bitsToGet) + { + _nextData = (_nextData << 8) | (_data[_bytePointer++] & 0xff); + _nextBits += 8; + } + + int code = (_nextData >> (_nextBits - _bitsToGet)) & _andTable[_bitsToGet - 9]; + _nextBits -= _bitsToGet; + + return code; + } + catch + { + return 257; + } + } + } + + readonly int[] _andTable = { 511, 1023, 2047, 4095 }; + byte[][] _stringTable; + byte[] _data; + int _tableIndex, _bitsToGet = 9; + int _bytePointer; + int _nextData = 0; + int _nextBits = 0; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/Chars.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/Chars.cs new file mode 100644 index 00000000..d39672ec --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/Chars.cs @@ -0,0 +1,189 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.IO +{ + /// + /// Character table by name. + /// + public sealed class Chars + { + // ReSharper disable InconsistentNaming + + /// + /// The EOF marker. + /// + public const char EOF = (char)65535; //unchecked((char)(-1)); + /// + /// The null byte. + /// + public const char NUL = '\0'; // EOF + /// + /// The carriage return character (ignored by lexer). + /// + public const char CR = '\x0D'; // ignored by lexer + /// + /// The line feed character. + /// + public const char LF = '\x0A'; // Line feed + /// + /// The bell character. + /// + public const char BEL = '\a'; // Bell + /// + /// The backspace character. + /// + public const char BS = '\b'; // Backspace + /// + /// The form feed character. + /// + public const char FF = '\f'; // Form feed + /// + /// The horizontal tab character. + /// + public const char HT = '\t'; // Horizontal tab + /// + /// The vertical tab character. + /// + public const char VT = '\v'; // Vertical tab + /// + /// The non-breakable space character (aka no-break space or non-breaking space). + /// + public const char NonBreakableSpace = (char)160; // char(160) + + // The following names come from "PDF Reference Third Edition" + // Appendix D.1, Latin Character Set and Encoding + /// + /// The space character. + /// + public const char SP = ' '; + /// + /// The double quote character. + /// + public const char QuoteDbl = '"'; + /// + /// The single quote character. + /// + public const char QuoteSingle = '\''; + /// + /// The left parenthesis. + /// + public const char ParenLeft = '('; + /// + /// The right parenthesis. + /// + public const char ParenRight = ')'; + /// + /// The left brace. + /// + public const char BraceLeft = '{'; + /// + /// The right brace. + /// + public const char BraceRight = '}'; + /// + /// The left bracket. + /// + public const char BracketLeft = '['; + /// + /// The right bracket. + /// + public const char BracketRight = ']'; + /// + /// The less-than sign. + /// + public const char Less = '<'; + /// + /// The greater-than sign. + /// + public const char Greater = '>'; + /// + /// The equal sign. + /// + public const char Equal = '='; + /// + /// The period. + /// + public const char Period = '.'; + /// + /// The semicolon. + /// + public const char Semicolon = ';'; + /// + /// The colon. + /// + public const char Colon = ':'; + /// + /// The slash. + /// + public const char Slash = '/'; + /// + /// The bar character. + /// + public const char Bar = '|'; + /// + /// The back slash. + /// + public const char BackSlash = '\\'; + /// + /// The percent sign. + /// + public const char Percent = '%'; + /// + /// The dollar sign. + /// + public const char Dollar = '$'; + /// + /// The at sign. + /// + public const char At = '@'; + /// + /// The number sign. + /// + public const char NumberSign = '#'; + /// + /// The question mark. + /// + public const char Question = '?'; + /// + /// The hyphen. + /// + public const char Hyphen = '-'; // char(45) + /// + /// The soft hyphen. + /// + public const char SoftHyphen = '­'; // char(173) + /// + /// The currency sign. + /// + public const char Currency = '¤'; + + // ReSharper restore InconsistentNaming + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/Lexer.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/Lexer.cs new file mode 100644 index 00000000..5bff4193 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/Lexer.cs @@ -0,0 +1,902 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using System.Diagnostics; +using System.Text; +using System.IO; +using PdfSharp.Internal; +using PdfSharp.Pdf.Internal; + +#pragma warning disable 1591 + +namespace PdfSharp.Pdf.IO +{ + /// + /// Lexical analyzer for PDF files. Technically a PDF file is a stream of bytes. Some chunks + /// of bytes represent strings in several encodings. The actual encoding depends on the + /// context where the string is used. Therefore the bytes are 'raw encoded' into characters, + /// i.e. a character or token read by the lexer has always character values in the range from + /// 0 to 255. + /// + public class Lexer + { + /// + /// Initializes a new instance of the Lexer class. + /// + public Lexer(Stream pdfInputStream) + { + _pdfSteam = pdfInputStream; + _pdfLength = (int)_pdfSteam.Length; + _idxChar = 0; + Position = 0; + } + + /// + /// Gets or sets the position within the PDF stream. + /// + public int Position + { + get { return _idxChar; } + set + { + _idxChar = value; + _pdfSteam.Position = value; + // ReadByte return -1 (eof) at the end of the stream. + _currChar = (char)_pdfSteam.ReadByte(); + _nextChar = (char)_pdfSteam.ReadByte(); + _token = new StringBuilder(); + } + } + + /// + /// Reads the next token and returns its type. If the token starts with a digit, the parameter + /// testReference specifies how to treat it. If it is false, the lexer scans for a single integer. + /// If it is true, the lexer checks if the digit is the prefix of a reference. If it is a reference, + /// the token is set to the object ID followed by the generation number separated by a blank + /// (the 'R' is omitted from the token). + /// + // /// Indicates whether to test the next token if it is a reference. + public Symbol ScanNextToken() + { + Again: + _token = new StringBuilder(); + + char ch = MoveToNonWhiteSpace(); + switch (ch) + { + case '%': + // Eat comments, the parser doesn't handle them + //return symbol = ScanComment(); + ScanComment(); + goto Again; + + case '/': + return _symbol = ScanName(); + + //case 'R': + // if (Lexer.IsWhiteSpace(nextChar)) + // { + // ScanNextChar(); + // return Symbol.R; + // } + // break; + + case '+': //TODO is it so easy? + case '-': + return _symbol = ScanNumber(); + + case '(': + return _symbol = ScanLiteralString(); + + case '[': + ScanNextChar(true); + return _symbol = Symbol.BeginArray; + + case ']': + ScanNextChar(true); + return _symbol = Symbol.EndArray; + + case '<': + if (_nextChar == '<') + { + ScanNextChar(true); + ScanNextChar(true); + return _symbol = Symbol.BeginDictionary; + } + return _symbol = ScanHexadecimalString(); + + case '>': + if (_nextChar == '>') + { + ScanNextChar(true); + ScanNextChar(true); + return _symbol = Symbol.EndDictionary; + } + ParserDiagnostics.HandleUnexpectedCharacter(_nextChar); + break; + + case '.': + return _symbol = ScanNumber(); + } + if (char.IsDigit(ch)) +#if true_ + return ScanNumberOrReference(); +#else + if (PeekReference()) + return _symbol = ScanNumber(); + else + return _symbol = ScanNumber(); +#endif + + if (char.IsLetter(ch)) + return _symbol = ScanKeyword(); + + if (ch == Chars.EOF) + return _symbol = Symbol.Eof; + + // #??? + + ParserDiagnostics.HandleUnexpectedCharacter(ch); + return _symbol = Symbol.None; + } + + /// + /// Reads the raw content of a stream. + /// + public byte[] ReadStream(int length) + { + int pos; + + // Skip illegal blanks behind stream. + while (_currChar == Chars.SP) + ScanNextChar(true); + + // Skip new line behind stream. + if (_currChar == Chars.CR) + { + if (_nextChar == Chars.LF) + pos = _idxChar + 2; + else + pos = _idxChar + 1; + } + else + pos = _idxChar + 1; + + _pdfSteam.Position = pos; + byte[] bytes = new byte[length]; + int read = _pdfSteam.Read(bytes, 0, length); + Debug.Assert(read == length); + // With corrupted files, read could be different from length. + if (bytes.Length != read) + { + Array.Resize(ref bytes, read); + } + + // Synchronize idxChar etc. + Position = pos + read; + return bytes; + } + + /// + /// Reads a string in raw encoding. + /// + public String ReadRawString(int position, int length) + { + _pdfSteam.Position = position; + byte[] bytes = new byte[length]; + _pdfSteam.Read(bytes, 0, length); + return PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); + } + + /// + /// Scans a comment line. + /// + public Symbol ScanComment() + { + Debug.Assert(_currChar == Chars.Percent); + + _token = new StringBuilder(); + while (true) + { + char ch = AppendAndScanNextChar(); + if (ch == Chars.LF || ch == Chars.EOF) + break; + } + // TODO: not correct + if (_token.ToString().StartsWith("%%EOF")) + return Symbol.Eof; + return _symbol = Symbol.Comment; + } + + /// + /// Scans a name. + /// + public Symbol ScanName() + { + Debug.Assert(_currChar == Chars.Slash); + + _token = new StringBuilder(); + while (true) + { + char ch = AppendAndScanNextChar(); + if (IsWhiteSpace(ch) || IsDelimiter(ch) || ch == Chars.EOF) + return _symbol = Symbol.Name; + + if (ch == '#') + { + ScanNextChar(true); + char[] hex = new char[2]; + hex[0] = _currChar; + hex[1] = _nextChar; + ScanNextChar(true); + // TODO Check syntax + ch = (char)(ushort)int.Parse(new string(hex), NumberStyles.AllowHexSpecifier); + _currChar = ch; + } + } + } + + /// + /// Scans a number. + /// + public Symbol ScanNumber() + { + // I found a PDF file created with Acrobat 7 with this entry + // /Checksum 2996984786 + // What is this? It is neither an integer nor a real. + // I introduced an UInteger... + bool period = false; + //bool sign; + + _token = new StringBuilder(); + char ch = _currChar; + if (ch == '+' || ch == '-') + { + //sign = true; + _token.Append(ch); + ch = ScanNextChar(true); + } + while (true) + { + if (char.IsDigit(ch)) + { + _token.Append(ch); + } + else if (ch == '.') + { + if (period) + ParserDiagnostics.ThrowParserException("More than one period in number."); + + period = true; + _token.Append(ch); + } + else + break; + ch = ScanNextChar(true); + } + + if (period) + return Symbol.Real; + long l = Int64.Parse(_token.ToString(), CultureInfo.InvariantCulture); + if (l >= Int32.MinValue && l <= Int32.MaxValue) + return Symbol.Integer; + if (l > 0 && l <= UInt32.MaxValue) + return Symbol.UInteger; + + // Got an AutoCAD PDF file that contains this: /C 264584027963392 + // Best we can do is to convert it to real value. + return Symbol.Real; + //thr ow new PdfReaderException("Number exceeds integer range."); + } + + public Symbol ScanNumberOrReference() + { + Symbol result = ScanNumber(); + if (result == Symbol.Integer) + { + int pos = Position; + string objectNumber = Token; + } + return result; + } + + /// + /// Scans a keyword. + /// + public Symbol ScanKeyword() + { + _token = new StringBuilder(); + char ch = _currChar; + // Scan token + while (true) + { + if (char.IsLetter(ch)) + _token.Append(ch); + else + break; + ch = ScanNextChar(false); + } + + // Check known tokens. + switch (_token.ToString()) + { + case "obj": + return _symbol = Symbol.Obj; + + case "endobj": + return _symbol = Symbol.EndObj; + + case "null": + return _symbol = Symbol.Null; + + case "true": + case "false": + return _symbol = Symbol.Boolean; + + case "R": + return _symbol = Symbol.R; + + case "stream": + return _symbol = Symbol.BeginStream; + + case "endstream": + return _symbol = Symbol.EndStream; + + case "xref": + return _symbol = Symbol.XRef; + + case "trailer": + return _symbol = Symbol.Trailer; + + case "startxref": + return _symbol = Symbol.StartXRef; + } + + // Anything else is treated as a keyword. Samples are f or n in iref. + return _symbol = Symbol.Keyword; + } + + /// + /// Scans a literal string, contained between "(" and ")". + /// + public Symbol ScanLiteralString() + { + // Reference: 3.2.3 String Objects / Page 53 + // Reference: TABLE 3.32 String Types / Page 157 + + Debug.Assert(_currChar == Chars.ParenLeft); + _token = new StringBuilder(); + int parenLevel = 0; + char ch = ScanNextChar(false); + + // Phase 1: deal with escape characters. + while (ch != Chars.EOF) + { + switch (ch) + { + case '(': + parenLevel++; + break; + + case ')': + if (parenLevel == 0) + { + ScanNextChar(false); + // Is goto evil? We could move Phase 2 code here or create a subroutine for Phase 1. + goto Phase2; + } + parenLevel--; + break; + + case '\\': + { + ch = ScanNextChar(false); + switch (ch) + { + case 'n': + ch = Chars.LF; + break; + + case 'r': + ch = Chars.CR; + break; + + case 't': + ch = Chars.HT; + break; + + case 'b': + ch = Chars.BS; + break; + + case 'f': + ch = Chars.FF; + break; + + case '(': + ch = Chars.ParenLeft; + break; + + case ')': + ch = Chars.ParenRight; + break; + + case '\\': + ch = Chars.BackSlash; + break; + + // AutoCAD PDFs my contain such strings: (\ ) + case ' ': + ch = ' '; + break; + + case Chars.CR: + case Chars.LF: + ch = ScanNextChar(false); + continue; + + default: + // TODO IsOctalDigit(ch). + if (char.IsDigit(ch) && _nextChar != '8' && _nextChar != '9') // First octal character. + { + //// Octal character code. + //if (ch >= '8') + // ParserDiagnostics.HandleUnexpectedCharacter(ch); + + int n = ch - '0'; + if (char.IsDigit(_nextChar) && _nextChar != '8' && _nextChar != '9') // Second octal character. + { + ch = ScanNextChar(false); + //if (ch >= '8') + // ParserDiagnostics.HandleUnexpectedCharacter(ch); + + n = n * 8 + ch - '0'; + if (char.IsDigit(_nextChar) && _nextChar != '8' && _nextChar != '9') // Third octal character. + { + ch = ScanNextChar(false); + //if (ch >= '8') + // ParserDiagnostics.HandleUnexpectedCharacter(ch); + + n = n * 8 + ch - '0'; + } + } + ch = (char)n; + } + else + { + // PDF 32000: "If the character following the REVERSE SOLIDUS is not one of those shown in Table 3, the REVERSE SOLIDUS shall be ignored." + //TODO + // Debug.As sert(false, "Not implemented; unknown escape character."); + // ParserDiagnostics.HandleUnexpectedCharacter(ch); + //GetType(); + } + break; + } + break; + } + default: + break; + } + + _token.Append(ch); + ch = ScanNextChar(false); + } + + // Phase 2: deal with UTF-16BE if necessary. + // UTF-16BE Unicode strings start with U+FEFF (""). There can be empty strings with UTF-16BE prefix. + Phase2: + if (_token.Length >= 2 && _token[0] == '\xFE' && _token[1] == '\xFF') + { + // Combine two ANSI characters to get one Unicode character. + StringBuilder temp = _token; + int length = temp.Length; + if ((length & 1) == 1) + { + // TODO What does the PDF Reference say about this case? Assume (char)0 or treat the file as corrupted? + temp.Append(0); + ++length; + DebugBreak.Break(); + } + _token = new StringBuilder(); + for (int i = 2; i < length; i += 2) + { + _token.Append((char)(256 * temp[i] + temp[i + 1])); + } + return _symbol = Symbol.UnicodeString; + } + // Adobe Reader also supports UTF-16LE. + if (_token.Length >= 2 && _token[0] == '\xFF' && _token[1] == '\xFE') + { + // Combine two ANSI characters to get one Unicode character. + StringBuilder temp = _token; + int length = temp.Length; + if ((length & 1) == 1) + { + // TODO What does the PDF Reference say about this case? Assume (char)0 or treat the file as corrupted? + temp.Append(0); + ++length; + DebugBreak.Break(); + } + _token = new StringBuilder(); + for (int i = 2; i < length; i += 2) + { + _token.Append((char)(256 * temp[i + 1] + temp[i])); + } + return _symbol = Symbol.UnicodeString; + } + return _symbol = Symbol.String; + } + + public Symbol ScanHexadecimalString() + { + Debug.Assert(_currChar == Chars.Less); + + _token = new StringBuilder(); + char[] hex = new char[2]; + ScanNextChar(true); + while (true) + { + MoveToNonWhiteSpace(); + if (_currChar == '>') + { + ScanNextChar(true); + break; + } + if (char.IsLetterOrDigit(_currChar)) + { + hex[0] = char.ToUpper(_currChar); + // Second char is optional in PDF spec. + if (char.IsLetterOrDigit(_nextChar)) + { + hex[1] = char.ToUpper(_nextChar); + ScanNextChar(true); + } + else + { + // We could check for ">" here and throw if we find anything else. The throw comes after the next iteration anyway. + hex[1] = '0'; + } + ScanNextChar(true); + + int ch = int.Parse(new string(hex), NumberStyles.AllowHexSpecifier); + _token.Append(Convert.ToChar(ch)); + } + else + ParserDiagnostics.HandleUnexpectedCharacter(_currChar); + } + string chars = _token.ToString(); + int count = chars.Length; + if (count > 2 && chars[0] == (char)0xFE && chars[1] == (char)0xFF) + { + Debug.Assert(count % 2 == 0); + _token.Length = 0; + for (int idx = 2; idx < count; idx += 2) + _token.Append((char)(chars[idx] * 256 + chars[idx + 1])); + return _symbol = Symbol.UnicodeHexString; + } + return _symbol = Symbol.HexString; + } + + /// + /// Move current position one character further in PDF stream. + /// + internal char ScanNextChar(bool handleCRLF) + { + if (_pdfLength <= _idxChar) + { + _currChar = Chars.EOF; + _nextChar = Chars.EOF; + } + else + { + _currChar = _nextChar; + _nextChar = (char)_pdfSteam.ReadByte(); + _idxChar++; + if (handleCRLF && _currChar == Chars.CR) + { + if (_nextChar == Chars.LF) + { + // Treat CR LF as LF. + _currChar = _nextChar; + _nextChar = (char)_pdfSteam.ReadByte(); + _idxChar++; + } + else + { + // Treat single CR as LF. + _currChar = Chars.LF; + } + } + } + return _currChar; + } + + ///// + ///// Resets the current token to the empty string. + ///// + //void ClearToken() + //{ + // _token.Length = 0; + //} + + bool PeekReference() + { + // A Reference has the form "nnn mmm R". The implementation of the parser used a + // reduce/shift algorithm in the first place. But this case is the only one we need to + // look ahead 3 tokens. + int positon = Position; + + // Skip digits. + while (char.IsDigit(_currChar)) + ScanNextChar(true); + + // Space expected. + if (_currChar != Chars.SP) + goto False; + + // Skip spaces. + while (_currChar == Chars.SP) + ScanNextChar(true); + + // Digit expected. + if (!char.IsDigit(_currChar)) + goto False; + + // Skip digits. + while (char.IsDigit(_currChar)) + ScanNextChar(true); + + // Space expected. + if (_currChar != Chars.SP) + goto False; + + // Skip spaces. + while (_currChar == Chars.SP) + ScanNextChar(true); + + // "R" expected. + // We can ignore _nextChar because there is no other valid token that starts with an 'R'. + if (_currChar != 'R') + goto False; + + Position = positon; + return true; + + False: + Position = positon; + return false; + } + + /// + /// Appends current character to the token and reads next one. + /// + internal char AppendAndScanNextChar() + { + if (_currChar == Chars.EOF) + ParserDiagnostics.ThrowParserException("Undetected EOF reached."); + + _token.Append(_currChar); + return ScanNextChar(true); + } + + /// + /// If the current character is not a white space, the function immediately returns it. + /// Otherwise the PDF cursor is moved forward to the first non-white space or EOF. + /// White spaces are NUL, HT, LF, FF, CR, and SP. + /// + public char MoveToNonWhiteSpace() + { + while (_currChar != Chars.EOF) + { + switch (_currChar) + { + case Chars.NUL: + case Chars.HT: + case Chars.LF: + case Chars.FF: + case Chars.CR: + case Chars.SP: + ScanNextChar(true); + break; + + case (char)11: + case (char)173: + ScanNextChar(true); + break; + + + default: + return _currChar; + } + } + return _currChar; + } + +#if DEBUG + public string SurroundingsOfCurrentPosition(bool hex) + { + const int range = 20; + int start = Math.Max(Position - range, 0); + int length = Math.Min(2 * range, PdfLength - start); + long posOld = _pdfSteam.Position; + _pdfSteam.Position = start; + byte[] bytes = new byte[length]; + _pdfSteam.Read(bytes, 0, length); + _pdfSteam.Position = posOld; + string result = ""; + if (hex) + { + for (int idx = 0; idx < length; idx++) + result += ((int)bytes[idx]).ToString("x2"); + //result += string.Format("{0:", (int) bytes[idx]); + } + else + { + for (int idx = 0; idx < length; idx++) + result += (char)bytes[idx]; + } + return result; + } +#endif + + /// + /// Gets the current symbol. + /// + public Symbol Symbol + { + get { return _symbol; } + set { _symbol = value; } + } + + /// + /// Gets the current token. + /// + public string Token + { + get { return _token.ToString(); } + } + + /// + /// Interprets current token as boolean literal. + /// + public bool TokenToBoolean + { + get + { + Debug.Assert(_token.ToString() == "true" || _token.ToString() == "false"); + return _token.ToString()[0] == 't'; + } + } + + /// + /// Interprets current token as integer literal. + /// + public int TokenToInteger + { + get + { + //Debug.As sert(_token.ToString().IndexOf('.') == -1); + return int.Parse(_token.ToString(), CultureInfo.InvariantCulture); + } + } + + /// + /// Interprets current token as unsigned integer literal. + /// + public uint TokenToUInteger + { + get + { + //Debug.As sert(_token.ToString().IndexOf('.') == -1); + return uint.Parse(_token.ToString(), CultureInfo.InvariantCulture); + } + } + + /// + /// Interprets current token as real or integer literal. + /// + public double TokenToReal + { + get { return double.Parse(_token.ToString(), CultureInfo.InvariantCulture); } + } + + /// + /// Interprets current token as object ID. + /// + public PdfObjectID TokenToObjectID + { + get + { + string[] numbers = Token.Split('|'); + int objectNumber = Int32.Parse(numbers[0]); + int generationNumber = Int32.Parse(numbers[1]); + return new PdfObjectID(objectNumber, generationNumber); + } + } + + /// + /// Indicates whether the specified character is a PDF white-space character. + /// + internal static bool IsWhiteSpace(char ch) + { + switch (ch) + { + case Chars.NUL: // 0 Null + case Chars.HT: // 9 Horizontal Tab + case Chars.LF: // 10 Line Feed + case Chars.FF: // 12 Form Feed + case Chars.CR: // 13 Carriage Return + case Chars.SP: // 32 Space + return true; + } + return false; + } + + /// + /// Indicates whether the specified character is a PDF delimiter character. + /// + internal static bool IsDelimiter(char ch) + { + switch (ch) + { + case '(': + case ')': + case '<': + case '>': + case '[': + case ']': + case '{': + case '}': + case '/': + case '%': + return true; + } + return false; + } + + /// + /// Gets the length of the PDF output. + /// + public int PdfLength + { + get { return _pdfLength; } + } + + readonly int _pdfLength; + int _idxChar; + char _currChar; + char _nextChar; + StringBuilder _token; + Symbol _symbol = Symbol.None; + + readonly Stream _pdfSteam; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs new file mode 100644 index 00000000..07b353a9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/Parser.cs @@ -0,0 +1,1818 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using PdfSharp.Internal; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.IO +{ + /* + Direct and indirect objects + + * If a simple object (boolean, integer, number, date, string, rectangle etc.) is referenced indirect, + the parser reads this objects immediately and consumes the indirection. + + * If a composite object (dictionary, array etc.) is referenced indirect, a PdfReference objects + is returned. + + * If a composite object is a direct object, no PdfReference is created and the object is + parsed immediately. + + * A reference to a non existing object is specified as legal, therefore null is returned. + */ + + /// + /// Provides the functionality to parse PDF documents. + /// + internal sealed class Parser + { + public Parser(PdfDocument document, Stream pdf) + { + _document = document; + _lexer = new Lexer(pdf); + _stack = new ShiftStack(); + } + + public Parser(PdfDocument document) + { + _document = document; + _lexer = document._lexer; + _stack = new ShiftStack(); + } + + /// + /// Sets PDF input stream position to the specified object. + /// + public int MoveToObject(PdfObjectID objectID) + { + int position = _document._irefTable[objectID].Position; + return _lexer.Position = position; + } + + public Symbol Symbol + { + get { return _lexer.Symbol; } + } + + public PdfObjectID ReadObjectNumber(int position) + { + _lexer.Position = position; + int objectNumber = ReadInteger(); + int generationNumber = ReadInteger(); +#if DEBUG && CORE + if (objectNumber == 1074) + GetType(); +#endif + return new PdfObjectID(objectNumber, generationNumber); + } + + + /// + /// Reads PDF object from input stream. + /// + /// Either the instance of a derived type or null. If it is null + /// an appropriate object is created. + /// The address of the object. + /// If true, specifies that all indirect objects + /// are included recursively. + /// If true, the objects is parsed from an object stream. + public PdfObject ReadObject(PdfObject pdfObject, PdfObjectID objectID, bool includeReferences, bool fromObjecStream) + { +#if DEBUG_ + Debug.WriteLine("ReadObject: " + objectID); + if (objectID.ObjectNumber == 20) + GetType(); +#endif + int objectNumber = objectID.ObjectNumber; + int generationNumber = objectID.GenerationNumber; + if (!fromObjecStream) + { + MoveToObject(objectID); + objectNumber = ReadInteger(); + generationNumber = ReadInteger(); + } +#if DEBUG + // The following assertion sometime failed (see below) + //Debug.Assert(objectID == new PdfObjectID(objectNumber, generationNumber)); + if (!fromObjecStream && objectID != new PdfObjectID(objectNumber, generationNumber)) + { + // A special kind of bug? Or is this an undocumented PDF feature? + // PDF4NET 2.6 provides a sample called 'Unicode', which produces a file 'unicode.pdf' + // The iref table of this file contains the following entries: + // iref + // 0 148 + // 0000000000 65535 f + // 0000000015 00000 n + // 0000000346 00000 n + // .... + // 0000083236 00000 n + // 0000083045 00000 n + // 0000083045 00000 n + // 0000083045 00000 n + // 0000083045 00000 n + // 0000080334 00000 n + // .... + // Object 84, 85, 86, and 87 maps to the same dictionary, but all PDF readers I tested + // ignores this mismatch! The following assertion failed about 50 times with this file. +#if true_ + string message = String.Format("xref entry {0} {1} maps to object {2} {3}.", + objectID.ObjectNumber, objectID.GenerationNumber, objectNumber, generationNumber); + Debug.Assert(false, message); +#endif + } +#endif + // Always use object ID from iref table (see above). + objectNumber = objectID.ObjectNumber; + generationNumber = objectID.GenerationNumber; +#if true_ + Debug.WriteLine(String.Format("obj: {0} {1}", objectNumber, generationNumber)); +#endif + if (!fromObjecStream) + ReadSymbol(Symbol.Obj); + + bool checkForStream = false; + Symbol symbol = ScanNextToken(); + switch (symbol) + { + case Symbol.BeginArray: + PdfArray array; + if (pdfObject == null) + array = new PdfArray(_document); + else + array = (PdfArray)pdfObject; + //PdfObject.RegisterObject(array, objectID, generation); + pdfObject = ReadArray(array, includeReferences); + pdfObject.SetObjectID(objectNumber, generationNumber); + break; + + case Symbol.BeginDictionary: + PdfDictionary dict; + if (pdfObject == null) + dict = new PdfDictionary(_document); + else + dict = (PdfDictionary)pdfObject; + //PdfObject.RegisterObject(dict, objectID, generation); + checkForStream = true; + pdfObject = ReadDictionary(dict, includeReferences); + pdfObject.SetObjectID(objectNumber, generationNumber); + break; + + // Acrobat 6 Professional proudly presents: The Null object! + // Even with a one-digit object number an indirect reference x 0 R to this object is + // one character larger than the direct use of null. Probable this is the reason why + // it is true that Acrobat Web Capture 6.0 creates this object, but obviously never + // creates a reference to it! + case Symbol.Null: + pdfObject = new PdfNullObject(_document); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + // Empty object. Invalid PDF, but we need to handle it. Treat as null object. + case Symbol.EndObj: + pdfObject = new PdfNullObject(_document); + pdfObject.SetObjectID(objectNumber, generationNumber); + return pdfObject; + + case Symbol.Boolean: + pdfObject = new PdfBooleanObject(_document, String.Compare(_lexer.Token, Boolean.TrueString, StringComparison.OrdinalIgnoreCase) == 0); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.Integer: + pdfObject = new PdfIntegerObject(_document, _lexer.TokenToInteger); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.UInteger: + pdfObject = new PdfUIntegerObject(_document, _lexer.TokenToUInteger); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.Real: + pdfObject = new PdfRealObject(_document, _lexer.TokenToReal); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.String: + case Symbol.UnicodeString: + case Symbol.HexString: + case Symbol.UnicodeHexString: + pdfObject = new PdfStringObject(_document, _lexer.Token); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.Name: + pdfObject = new PdfNameObject(_document, _lexer.Token); + pdfObject.SetObjectID(objectNumber, generationNumber); + if (!fromObjecStream) + ReadSymbol(Symbol.EndObj); + return pdfObject; + + case Symbol.Keyword: + // Should not come here anymore. + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + break; + + default: + // Should not come here anymore. + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + break; + } + symbol = ScanNextToken(); + if (symbol == Symbol.BeginStream) + { + PdfDictionary dict = (PdfDictionary)pdfObject; + Debug.Assert(checkForStream, "Unexpected stream..."); +#if true_ + ReadStream(dict); +#else + int length = GetStreamLength(dict); + byte[] bytes = _lexer.ReadStream(length); +#if true_ + if (dict.Elements.GetString("/Filter") == "/FlateDecode") + { + if (dict.Elements["/Subtype"] == null) + { + try + { + byte[] decoded = Filtering.FlateDecode.Decode(bytes); + if (decoded.Length == 0) + goto End; + string pageContent = Filtering.FlateDecode.DecodeToString(bytes); + if (pageContent.Length > 100) + pageContent = pageContent.Substring(pageContent.Length - 100); + pageContent.GetType(); + bytes = decoded; + dict.Elements.Remove("/Filter"); + dict.Elements.SetInteger("/Length", bytes.Length); + } + catch + { + } + } + End: ; + } +#endif + PdfDictionary.PdfStream stream = new PdfDictionary.PdfStream(bytes, dict); + dict.Stream = stream; + ReadSymbol(Symbol.EndStream); + symbol = ScanNextToken(); +#endif + } + if (!fromObjecStream && symbol != Symbol.EndObj) + ParserDiagnostics.ThrowParserException(PSSR.UnexpectedToken(_lexer.Token)); + return pdfObject; + } + + //public PdfObject ReadObject(PdfObject obj, bool includeReferences) + + /// + /// Reads the stream of a dictionary. + /// + private void ReadStream(PdfDictionary dict) + { + Symbol symbol = _lexer.Symbol; + Debug.Assert(symbol == Symbol.BeginStream); + int length = GetStreamLength(dict); + byte[] bytes = _lexer.ReadStream(length); + PdfDictionary.PdfStream stream = new PdfDictionary.PdfStream(bytes, dict); + Debug.Assert(dict.Stream == null, "Dictionary already has a stream."); + dict.Stream = stream; + ReadSymbol(Symbol.EndStream); + ScanNextToken(); + } + + // HACK: Solve problem more general. + private int GetStreamLength(PdfDictionary dict) + { + if (dict.Elements["/F"] != null) + throw new NotImplementedException("File streams are not yet implemented."); + + PdfItem value = dict.Elements["/Length"]; + if (value is PdfInteger) + return Convert.ToInt32(value); + + PdfReference reference = value as PdfReference; + if (reference != null) + { + ParserState state = SaveState(); + object length = ReadObject(null, reference.ObjectID, false, false); + RestoreState(state); + int len = ((PdfIntegerObject)length).Value; + dict.Elements["/Length"] = new PdfInteger(len); + return len; + } + throw new InvalidOperationException("Cannot retrieve stream length."); + } + + public PdfArray ReadArray(PdfArray array, bool includeReferences) + { + Debug.Assert(Symbol == Symbol.BeginArray); + + if (array == null) + array = new PdfArray(_document); + + int sp = _stack.SP; + ParseObject(Symbol.EndArray); + int count = _stack.SP - sp; + PdfItem[] items = _stack.ToArray(sp, count); + _stack.Reduce(count); + for (int idx = 0; idx < count; idx++) + { + PdfItem val = items[idx]; + if (includeReferences && val is PdfReference) + val = ReadReference((PdfReference)val, true); + array.Elements.Add(val); + } + return array; + } + +#if DEBUG_ + static int ReadDictionaryCounter; +#endif + + internal PdfDictionary ReadDictionary(PdfDictionary dict, bool includeReferences) + { + Debug.Assert(Symbol == Symbol.BeginDictionary); + +#if DEBUG_ + ReadDictionaryCounter++; + Debug.WriteLine(ReadDictionaryCounter.ToString()); + if (ReadDictionaryCounter == 101) + GetType(); +#endif + + if (dict == null) + dict = new PdfDictionary(_document); + DictionaryMeta meta = dict.Meta; + + int sp = _stack.SP; + ParseObject(Symbol.EndDictionary); + int count = _stack.SP - sp; + Debug.Assert(count % 2 == 0); + PdfItem[] items = _stack.ToArray(sp, count); + _stack.Reduce(count); + for (int idx = 0; idx < count; idx += 2) + { + PdfItem val = items[idx]; + if (!(val is PdfName)) + ParserDiagnostics.ThrowParserException("Name expected."); // TODO L10N using PSSR. + + string key = val.ToString(); + val = items[idx + 1]; + if (includeReferences && val is PdfReference) + val = ReadReference((PdfReference)val, true); + dict.Elements[key] = val; + } + return dict; + } + +#if DEBUG_ + static int ParseObjectCounter; +#endif + + /// + /// Parses whatever comes until the specified stop symbol is reached. + /// + private void ParseObject(Symbol stop) + { +#if DEBUG_ + ParseObjectCounter++; + Debug.WriteLine(ParseObjectCounter.ToString()); + if (ParseObjectCounter == 178) + GetType(); +#endif + Symbol symbol; + while ((symbol = ScanNextToken()) != Symbol.Eof) + { + if (symbol == stop) + return; + + switch (symbol) + { + case Symbol.Comment: + // ignore comments + break; + + case Symbol.Null: + _stack.Shift(PdfNull.Value); + break; + + case Symbol.Boolean: + _stack.Shift(new PdfBoolean(_lexer.TokenToBoolean)); + break; + + case Symbol.Integer: + _stack.Shift(new PdfInteger(_lexer.TokenToInteger)); + break; + + case Symbol.UInteger: + _stack.Shift(new PdfUInteger(_lexer.TokenToUInteger)); + break; + + case Symbol.Real: + _stack.Shift(new PdfReal(_lexer.TokenToReal)); + break; + + case Symbol.String: + //stack.Shift(new PdfString(lexer.Token, PdfStringFlags.PDFDocEncoding)); + _stack.Shift(new PdfString(_lexer.Token, PdfStringFlags.RawEncoding)); + break; + + case Symbol.UnicodeString: + _stack.Shift(new PdfString(_lexer.Token, PdfStringFlags.Unicode)); + break; + + case Symbol.HexString: + _stack.Shift(new PdfString(_lexer.Token, PdfStringFlags.HexLiteral)); + break; + + case Symbol.UnicodeHexString: + _stack.Shift(new PdfString(_lexer.Token, PdfStringFlags.Unicode | PdfStringFlags.HexLiteral)); + break; + + case Symbol.Name: + _stack.Shift(new PdfName(_lexer.Token)); + break; + + case Symbol.R: + { + Debug.Assert(_stack.GetItem(-1) is PdfInteger && _stack.GetItem(-2) is PdfInteger); + PdfObjectID objectID = new PdfObjectID(_stack.GetInteger(-2), _stack.GetInteger(-1)); + + PdfReference iref = _document._irefTable[objectID]; + if (iref == null) + { + // If a document has more than one PdfXRefTable it is possible that the first trailer has + // indirect references to objects whose iref entry is not yet read in. + if (_document._irefTable.IsUnderConstruction) + { + // XRefTable not complete when trailer is read. Create temporary irefs that are + // removed later in PdfTrailer.FixXRefs. + iref = new PdfReference(objectID, 0); + _stack.Reduce(iref, 2); + break; + } + // PDF Reference section 3.2.9: + // An indirect reference to an undefined object is not an error; + // it is simply treated as a reference to the null object. + _stack.Reduce(PdfNull.Value, 2); + // Let's see what null objects are good for... + //Debug.Assert(false, "Null object detected!"); + //stack.Reduce(PdfNull.Value, 2); + } + else + _stack.Reduce(iref, 2); + break; + } + + case Symbol.BeginArray: + PdfArray array = new PdfArray(_document); + ReadArray(array, false); + _stack.Shift(array); + break; + + case Symbol.BeginDictionary: + PdfDictionary dict = new PdfDictionary(_document); + ReadDictionary(dict, false); + _stack.Shift(dict); + break; + + case Symbol.BeginStream: + throw new NotImplementedException(); + + // Not expected here: + //case Symbol.None: + //case Symbol.Keyword: + //case Symbol.EndStream: + //case Symbol.EndArray: + //case Symbol.EndDictionary: + //case Symbol.Obj: + //case Symbol.EndObj: + //case Symbol.XRef: + //case Symbol.Trailer: + //case Symbol.StartXRef: + //case Symbol.Eof: + default: + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + SkipCharsUntil(stop); + return; + } + } + ParserDiagnostics.ThrowParserException("Unexpected end of file."); // TODO L10N using PSSR. + } + + private Symbol ScanNextToken() + { + return _lexer.ScanNextToken(); + } + + private Symbol ScanNextToken(out string token) + { + Symbol symbol = _lexer.ScanNextToken(); + token = _lexer.Token; + return symbol; + } + + private Symbol SkipCharsUntil(Symbol stop) + { + Symbol symbol; + switch (stop) + { + case Symbol.EndDictionary: + return SkipCharsUntil(">>", stop); + + default: + do + { + symbol = ScanNextToken(); + } while (symbol != stop && symbol != Symbol.Eof); + return symbol; + } + } + + private Symbol SkipCharsUntil(string text, Symbol stop) + { + int length = text.Length; + int idx = 0; + char ch; + while ((ch = _lexer.ScanNextChar(true)) != Chars.EOF) + { + if (ch == text[idx]) + { + if (idx + 1 == length) + { + _lexer.ScanNextChar(true); + return stop; + } + idx++; + } + else + idx = 0; + } + return Symbol.Eof; + } + + //protected Symbol ScanNextToken(out string token, bool testReference) + //{ + // Symbol symbol = lexer.ScanNextToken(testReference); + // token = lexer.Token; + // return symbol; + //} + + // internal object ReadObject(int position) + // { + // lexer.Position = position; + // return ReadObject(false); + // } + // + // internal virtual object ReadObject(bool directObject) + // { + // throw new InvalidOperationException("PdfParser.ReadObject() base class called"); + // } + + /// + /// Reads the object ID and the generation and sets it into the specified object. + /// + private void ReadObjectID(PdfObject obj) + { + int objectNubmer = ReadInteger(); + int generationNumber = ReadInteger(); + ReadSymbol(Symbol.Obj); + if (obj != null) + obj.SetObjectID(objectNubmer, generationNumber); + } + + private PdfItem ReadReference(PdfReference iref, bool includeReferences) + { + throw new NotImplementedException("ReadReference"); + } + + /// + /// Reads the next symbol that must be the specified one. + /// + private Symbol ReadSymbol(Symbol symbol) + { + if (symbol == Symbol.EndStream) + { + Skip: + char ch = _lexer.MoveToNonWhiteSpace(); + + if (ch == Chars.EOF) + ParserDiagnostics.HandleUnexpectedCharacter(ch); + + if (ch != 'e') + { + _lexer.ScanNextChar(false); + goto Skip; + } + } + Symbol current = _lexer.ScanNextToken(); + if (symbol != current) + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + return current; + } + + /// + /// Reads the next token that must be the specified one. + /// + private Symbol ReadToken(string token) + { + Symbol current = _lexer.ScanNextToken(); + if (token != _lexer.Token) + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + return current; + } + + /// + /// Reads a name from the PDF data stream. The preceding slash is part of the result string. + /// + private string ReadName() + { + string name; + Symbol symbol = ScanNextToken(out name); + if (symbol != Symbol.Name) + ParserDiagnostics.HandleUnexpectedToken(name); + return name; + } + + /* + /// + /// Reads a string immediately or (optionally) indirectly from the PDF data stream. + /// + protected string ReadString(bool canBeIndirect) + { + Symbol symbol = Symbol.None; //lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.String || symbol == Symbol.UnicodeString || symbol == Symbol.HexString || symbol == Symbol.UnicodeHexString) + return lexer.Token; + else if (symbol == Symbol.R) + { + int position = lexer.Position; + MoveToObject(lexer.Token); + ReadObjectID(null); + string s = ReadString(); + ReadSymbol(Symbol.EndObj); + lexer.Position = position; + return s; + } + thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + protected string ReadString() + { + return ReadString(false); + } + + /// + /// Reads a string immediately or (optionally) indirectly from the PDF data stream. + /// + protected bool ReadBoolean(bool canBeIndirect) + { + Symbol symbol = lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.Boolean) + return lexer.TokenToBoolean; + else if (symbol == Symbol.R) + { + int position = lexer.Position; + MoveToObject(lexer.Token); + ReadObjectID(null); + bool b = ReadBoolean(); + ReadSymbol(Symbol.EndObj); + lexer.Position = position; + return b; + } + thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + protected bool ReadBoolean() + { + return ReadBoolean(false); + } + */ + + /// + /// Reads an integer value directly from the PDF data stream. + /// + private int ReadInteger(bool canBeIndirect) + { + Symbol symbol = _lexer.ScanNextToken(); + if (symbol == Symbol.Integer) + return _lexer.TokenToInteger; + + if (symbol == Symbol.R) + { + int position = _lexer.Position; + // MoveToObject(lexer.Token); + ReadObjectID(null); + int n = ReadInteger(); + ReadSymbol(Symbol.EndObj); + _lexer.Position = position; + return n; + } + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + return 0; + } + + private int ReadInteger() + { + return ReadInteger(false); + } + + // /// + // /// Reads a real value directly or (optionally) indirectly from the PDF data stream. + // /// + // double ReadReal(bool canBeIndirect) + // { + // Symbol symbol = lexer.ScanNextToken(canBeIndirect); + // if (symbol == Symbol.Real || symbol == Symbol.Integer) + // return lexer.TokenToReal; + // else if (symbol == Symbol.R) + // { + // int position = lexer.Position; + //// MoveToObject(lexer.Token); + // ReadObjectID(null); + // double f = ReadReal(); + // ReadSymbol(Symbol.EndObj); + // lexer.Position = position; + // return f; + // } + // thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + // } + // + // double ReadReal() + // { + // return ReadReal(false); + // } + + // /// + // /// Reads an object from the PDF input stream. If the object has a specialized parser, it it used. + // /// + // public static PdfObject ReadObject(PdfObject pdfObject, PdfObjectID objectID) + // { + // if (pdfObject == null) + // thr ow new ArgumentNullException("pdfObject"); + // if (pdfObject.Document == null) + // th row new ArgumentException(PSSR.OwningDocumentRequired, "pdfObject"); + // + // Type type = pdfObject.GetType(); + // PdfParser parser = CreateParser(pdfObject.Document, type); + // return parser.ReadObject(pdfObject, objectID, false); + // } + + /// + /// Reads an object from the PDF input stream using the default parser. + /// + public static PdfObject ReadObject(PdfDocument owner, PdfObjectID objectID) + { + if (owner == null) + throw new ArgumentNullException("owner"); + + Parser parser = new Parser(owner); + return parser.ReadObject(null, objectID, false, false); + } + + /// + /// Reads the irefs from the compressed object with the specified index in the object stream + /// of the object with the specified object id. + /// + internal void ReadIRefsFromCompressedObject(PdfObjectID objectID) + { + PdfReference iref; + + Debug.Assert(_document._irefTable.ObjectTable.ContainsKey(objectID)); + if (!_document._irefTable.ObjectTable.TryGetValue(objectID, out iref)) + { + // We should never come here because the object stream must be a type 1 entry in the xref stream + // and iref was created before. + throw new NotImplementedException("This case is not coded or something else went wrong"); + } + + // Read in object stream object when we come here for the very first time. + if (iref.Value == null) + { + try + { + Debug.Assert(_document._irefTable.Contains(iref.ObjectID)); + PdfDictionary pdfObject = (PdfDictionary)ReadObject(null, iref.ObjectID, false, false); + PdfObjectStream objectStream = new PdfObjectStream(pdfObject); + Debug.Assert(objectStream.Reference == iref); + // objectStream.Reference = iref; Superfluous, see Assert in line before. + Debug.Assert(objectStream.Reference.Value != null, "Something went wrong."); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + throw; + } + } + Debug.Assert(iref.Value != null); + + PdfObjectStream objectStreamStream = iref.Value as PdfObjectStream; + if (objectStreamStream == null) + { + Debug.Assert(((PdfDictionary)iref.Value).Elements.GetName("/Type") == "/ObjStm"); + + objectStreamStream = new PdfObjectStream((PdfDictionary)iref.Value); + Debug.Assert(objectStreamStream.Reference == iref); + // objectStream.Reference = iref; Superfluous, see Assert in line before. + Debug.Assert(objectStreamStream.Reference.Value != null, "Something went wrong."); + } + Debug.Assert(objectStreamStream != null); + + + //PdfObjectStream objectStreamStream = (PdfObjectStream)iref.Value; + if (objectStreamStream == null) + throw new Exception("Something went wrong here."); + objectStreamStream.ReadReferences(_document._irefTable); + } + + /// + /// Reads the compressed object with the specified index in the object stream + /// of the object with the specified object id. + /// + internal PdfReference ReadCompressedObject(PdfObjectID objectID, int index) + { + PdfReference iref; +#if true + Debug.Assert(_document._irefTable.ObjectTable.ContainsKey(objectID)); + if (!_document._irefTable.ObjectTable.TryGetValue(objectID, out iref)) + { + throw new NotImplementedException("This case is not coded or something else went wrong"); + } +#else + // We should never come here because the object stream must be a type 1 entry in the xref stream + // and iref was created before. + + // Has the specified object already an iref in the object table? + if (!_document._irefTable.ObjectTable.TryGetValue(objectID, out iref)) + { + try + { +#if true_ + iref = new PdfReference(objectID,); + iref.ObjectID = objectID; + _document._irefTable.Add(os); +#else + PdfDictionary dict = (PdfDictionary)ReadObject(null, objectID, false, false); + PdfObjectStream os = new PdfObjectStream(dict); + iref = new PdfReference(os); + iref.ObjectID = objectID; + _document._irefTable.Add(os); +#endif + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + throw; + } + } +#endif + + // Read in object stream object when we come here for the very first time. + if (iref.Value == null) + { + try + { + Debug.Assert(_document._irefTable.Contains(iref.ObjectID)); + PdfDictionary pdfObject = (PdfDictionary)ReadObject(null, iref.ObjectID, false, false); + PdfObjectStream objectStream = new PdfObjectStream(pdfObject); + Debug.Assert(objectStream.Reference == iref); + // objectStream.Reference = iref; Superfluous, see Assert in line before. + Debug.Assert(objectStream.Reference.Value != null, "Something went wrong."); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + throw; + } + } + Debug.Assert(iref.Value != null); + + PdfObjectStream objectStreamStream = iref.Value as PdfObjectStream; + if (objectStreamStream == null) + { + Debug.Assert(((PdfDictionary)iref.Value).Elements.GetName("/Type") == "/ObjStm"); + + objectStreamStream = new PdfObjectStream((PdfDictionary)iref.Value); + Debug.Assert(objectStreamStream.Reference == iref); + // objectStream.Reference = iref; Superfluous, see Assert in line before. + Debug.Assert(objectStreamStream.Reference.Value != null, "Something went wrong."); + } + Debug.Assert(objectStreamStream != null); + + + //PdfObjectStream objectStreamStream = (PdfObjectStream)iref.Value; + if (objectStreamStream == null) + throw new Exception("Something went wrong here."); + return objectStreamStream.ReadCompressedObject(index); + } + + /// + /// Reads the compressed object with the specified number at the given offset. + /// The parser must be initialized with the stream an object stream object. + /// + internal PdfReference ReadCompressedObject(int objectNumber, int offset) + { +#if DEBUG__ + if (objectNumber == 1034) + GetType(); +#endif + // Generation is always 0 for compressed objects. + PdfObjectID objectID = new PdfObjectID(objectNumber); + _lexer.Position = offset; + PdfObject obj = ReadObject(null, objectID, false, true); + return obj.Reference; + } + + /// + /// Reads the object stream header as pairs of integers from the beginning of the + /// stream of an object stream. Parameter first is the value of the First entry of + /// the object stream object. + /// + internal int[][] ReadObjectStreamHeader(int n, int first) + { + // TODO: Concept for general error handling. + // If the stream is corrupted a lot of things can go wrong here. + // Make it sense to do a more detailed error checking? + + // Create n pairs of integers with object number and offset. + int[][] header = new int[n][]; + for (int idx = 0; idx < n; idx++) + { + int number = ReadInteger(); +#if DEBUG + if (number == 1074) + GetType(); +#endif + int offset = ReadInteger() + first; // Calculate absolute offset. + header[idx] = new int[] { number, offset }; + } + return header; + } + + /// + /// Reads the cross-reference table(s) and their trailer dictionary or + /// cross-reference streams. + /// + internal PdfTrailer ReadTrailer() + { + int length = _lexer.PdfLength; + + // Implementation note 18 Appendix H: + // Acrobat viewers require only that the %%EOF marker appear somewhere within the last 1024 bytes of the file. + int idx; + if (length < 1030) + { + // Reading the final 30 bytes should work for all files. But often it does not. + string trail = _lexer.ReadRawString(length - 31, 30); //lexer.Pdf.Substring(length - 30); + idx = trail.LastIndexOf("startxref", StringComparison.Ordinal); + _lexer.Position = length - 31 + idx; + } + else + { + // For larger files we read 1 kiB - in most cases we find "startxref" in that range. + string trail = _lexer.ReadRawString(length - 1031, 1030); + idx = trail.LastIndexOf("startxref", StringComparison.Ordinal); + _lexer.Position = length - 1031 + idx; + } + + // SAP sometimes creates files with a size of several MByte and places "startxref" somewhere in the middle... + if (idx == -1) + { + // If "startxref" was still not found yet, read the file completely. + string trail = _lexer.ReadRawString(0, length); + idx = trail.LastIndexOf("startxref", StringComparison.Ordinal); + _lexer.Position = idx; + } + if (idx == -1) + throw new Exception("The StartXRef table could not be found, the file cannot be opened."); + + ReadSymbol(Symbol.StartXRef); + _lexer.Position = ReadInteger(); + + // Read all trailers. + while (true) + { + PdfTrailer trailer = ReadXRefTableAndTrailer(_document._irefTable); + // 1st trailer seems to be the best. + if (_document._trailer == null) + _document._trailer = trailer; + int prev = trailer != null ? trailer.Elements.GetInteger(PdfTrailer.Keys.Prev) : 0; + if (prev == 0) + break; + //if (prev > lexer.PdfLength) + // break; + _lexer.Position = prev; + } + + return _document._trailer; + } + + /// + /// Reads cross reference table(s) and trailer(s). + /// + private PdfTrailer ReadXRefTableAndTrailer(PdfCrossReferenceTable xrefTable) + { + Debug.Assert(xrefTable != null); + + Symbol symbol = ScanNextToken(); + + if (symbol == Symbol.XRef) // Is it a cross-reference table? + { + // Reference: 3.4.3 Cross-Reference Table / Page 93 + while (true) + { + symbol = ScanNextToken(); + if (symbol == Symbol.Integer) + { + int start = _lexer.TokenToInteger; + int length = ReadInteger(); + for (int id = start; id < start + length; id++) + { + int position = ReadInteger(); + int generation = ReadInteger(); + ReadSymbol(Symbol.Keyword); + string token = _lexer.Token; + // Skip start entry. + if (id == 0) + continue; + // Skip unused entries. + if (token != "n") + continue; +#if true + //!!!new 2018-03-14 begin + // Check if the object at the address has the correct ID and generation. + int idToUse = id; + int idChecked, generationChecked; + if (!CheckXRefTableEntry(position, id, generation, out idChecked, out generationChecked)) + { + // Found the keyword "obj", but ID or generation did not match. + // There is a tool where ID is off by one. In this case we use the ID from the object, not the ID from the XRef table. + if (generation == generationChecked && id == idChecked + 1) + idToUse = idChecked; + else + ParserDiagnostics.ThrowParserException("Invalid entry in XRef table, ID=" + id + ", Generation=" + generation + ", Position=" + position + ", ID of referenced object=" + idChecked + ", Generation of referenced object=" + generationChecked); // TODO L10N using PSSR. + } + //!!!new 2018-03-14 end +#endif + // Even if it is restricted, an object can exist in more than one subsection. + // (PDF Reference Implementation Notes 15). + PdfObjectID objectID = new PdfObjectID(idToUse, generation); + // Ignore the latter one. + if (xrefTable.Contains(objectID)) + continue; + xrefTable.Add(new PdfReference(objectID, position)); + } + } + else if (symbol == Symbol.Trailer) + { + ReadSymbol(Symbol.BeginDictionary); + PdfTrailer trailer = new PdfTrailer(_document); + ReadDictionary(trailer, false); + return trailer; + } + else + ParserDiagnostics.HandleUnexpectedToken(_lexer.Token); + } + } + // ReSharper disable once RedundantIfElseBlock because of code readability. + else if (symbol == Symbol.Integer) // Is it an cross-reference stream? + { + // Reference: 3.4.7 Cross-Reference Streams / Page 93 + // TODO: Handle PDF files larger than 2 GiB, see implementation note 21 in Appendix H. + + // The parsed integer is the object id of the cross-reference stream. + return ReadXRefStream(xrefTable); + } + return null; + } + + /// + /// Checks the x reference table entry. Returns true if everything is correct. + /// Return false if the keyword "obj" was found, but ID or Generation are incorrect. + /// Throws an exception otherwise. + /// + /// The position where the object is supposed to be. + /// The ID from the XRef table. + /// The generation from the XRef table. + /// The identifier found in the PDF file. + /// The generation found in the PDF file. + /// + private bool CheckXRefTableEntry(int position, int id, int generation, out int idChecked, out int generationChecked) + { + int origin = _lexer.Position; + idChecked = -1; + generationChecked = -1; + try + { + _lexer.Position = position; + idChecked = ReadInteger(); + generationChecked = ReadInteger(); + //// TODO Should we use ScanKeyword here? + //ReadKSymbol(Symbol.Keyword); + //string token = _lexer.Token; + Symbol symbol = _lexer.ScanNextToken(); + if (symbol != Symbol.Obj) + ParserDiagnostics.ThrowParserException("Invalid entry in XRef table, ID=" + id + ", Generation=" + generation + ", Position=" + position); // TODO L10N using PSSR. + + if (id != idChecked || generation != generationChecked) + return false; + } + catch (PdfReaderException) + { + throw; + } + catch (Exception ex) + { + ParserDiagnostics.ThrowParserException("Invalid entry in XRef table, ID=" + id + ", Generation=" + generation + ", Position=" + position, ex); // TODO L10N using PSSR. + } + finally + { + _lexer.Position = origin; + } + return true; + } + + /// + /// Reads cross reference stream(s). + /// + private PdfTrailer ReadXRefStream(PdfCrossReferenceTable xrefTable) + { + // Read cross reference stream. + //Debug.Assert(_lexer.Symbol == Symbol.Integer); + + int number = _lexer.TokenToInteger; + int generation = ReadInteger(); + Debug.Assert(generation == 0); + + ReadSymbol(Symbol.Obj); + ReadSymbol(Symbol.BeginDictionary); + PdfObjectID objectID = new PdfObjectID(number, generation); + + PdfCrossReferenceStream xrefStream = new PdfCrossReferenceStream(_document); + + ReadDictionary(xrefStream, false); + ReadSymbol(Symbol.BeginStream); + ReadStream(xrefStream); + + //xrefTable.Add(new PdfReference(objectID, position)); + PdfReference iref = new PdfReference(xrefStream); + iref.ObjectID = objectID; + iref.Value = xrefStream; + xrefTable.Add(iref); + + Debug.Assert(xrefStream.Stream != null); + //string sValue = new RawEncoding().GetString(xrefStream.Stream.UnfilteredValue,); + //sValue.GetType(); + byte[] bytesRaw = xrefStream.Stream.UnfilteredValue; + byte[] bytes = bytesRaw; + + // HACK: Should be done in UnfilteredValue. + if (xrefStream.Stream.HasDecodeParams) + { + int predictor = xrefStream.Stream.DecodePredictor; + int columns = xrefStream.Stream.DecodeColumns; + bytes = DecodeCrossReferenceStream(bytesRaw, columns, predictor); + } + +#if DEBUG_ + for (int idx = 0; idx < bytes.Length; idx++) + { + if (idx % 4 == 0) + Console.WriteLine(); + Console.Write("{0:000} ", (int)bytes[idx]); + } + Console.WriteLine(); +#endif + + // bytes.GetType(); + // Add to table. + // xrefTable.Add(new PdfReference(objectID, -1)); + + int size = xrefStream.Elements.GetInteger(PdfCrossReferenceStream.Keys.Size); + PdfArray index = xrefStream.Elements.GetValue(PdfCrossReferenceStream.Keys.Index) as PdfArray; + int prev = xrefStream.Elements.GetInteger(PdfCrossReferenceStream.Keys.Prev); + PdfArray w = (PdfArray)xrefStream.Elements.GetValue(PdfCrossReferenceStream.Keys.W); + + // E.g.: W[1 2 1] Index[7 12] Size 19 + + // Setup subsections. + int subsectionCount; + int[][] subsections = null; + int subsectionEntryCount = 0; + if (index == null) + { + // Setup with default values. + subsectionCount = 1; + subsections = new int[subsectionCount][]; + subsections[0] = new int[] { 0, size }; // HACK: What is size? Contradiction in PDF reference. + subsectionEntryCount = size; + } + else + { + // Read subsections from array. + Debug.Assert(index.Elements.Count % 2 == 0); + subsectionCount = index.Elements.Count / 2; + subsections = new int[subsectionCount][]; + for (int idx = 0; idx < subsectionCount; idx++) + { + subsections[idx] = new int[] { index.Elements.GetInteger(2 * idx), index.Elements.GetInteger(2 * idx + 1) }; + subsectionEntryCount += subsections[idx][1]; + } + } + + // W key. + Debug.Assert(w.Elements.Count == 3); + int[] wsize = { w.Elements.GetInteger(0), w.Elements.GetInteger(1), w.Elements.GetInteger(2) }; + int wsum = StreamHelper.WSize(wsize); + if (wsum * subsectionEntryCount != bytes.Length) + GetType(); + Debug.Assert(wsum * subsectionEntryCount == bytes.Length, "Check implementation here."); + int testcount = subsections[0][1]; + int[] currentSubsection = subsections[0]; +#if DEBUG && CORE + if (PdfDiagnostics.TraceXrefStreams) + { + for (int idx = 0; idx < testcount; idx++) + { + uint field1 = StreamHelper.ReadBytes(bytes, idx * wsum, wsize[0]); + uint field2 = StreamHelper.ReadBytes(bytes, idx * wsum + wsize[0], wsize[1]); + uint field3 = StreamHelper.ReadBytes(bytes, idx * wsum + wsize[0] + wsize[1], wsize[2]); + string res = String.Format("{0,2:00}: {1} {2,5} {3} // ", idx, field1, field2, field3); + switch (field1) + { + case 0: + res += "Fee list: object number, generation number"; + break; + + case 1: + res += "Not compresed: offset, generation number"; + break; + + case 2: + res += "Compressed: object stream object number, index in stream"; + break; + + default: + res += "??? Type undefined"; + break; + } + Debug.WriteLine(res); + } + } +#endif + + int index2 = -1; + for (int ssc = 0; ssc < subsectionCount; ssc++) + { + int abc = subsections[ssc][1]; + for (int idx = 0; idx < abc; idx++) + { + index2++; + + PdfCrossReferenceStream.CrossReferenceStreamEntry item = + new PdfCrossReferenceStream.CrossReferenceStreamEntry(); + + item.Type = StreamHelper.ReadBytes(bytes, index2 * wsum, wsize[0]); + item.Field2 = StreamHelper.ReadBytes(bytes, index2 * wsum + wsize[0], wsize[1]); + item.Field3 = StreamHelper.ReadBytes(bytes, index2 * wsum + wsize[0] + wsize[1], wsize[2]); + + xrefStream.Entries.Add(item); + + switch (item.Type) + { + case 0: + // Nothing to do, not needed. + break; + + case 1: // offset / generation number + //// Even it is restricted, an object can exists in more than one subsection. + //// (PDF Reference Implementation Notes 15). + + int position = (int)item.Field2; + objectID = ReadObjectNumber(position); +#if DEBUG + if (objectID.ObjectNumber == 1074) + GetType(); +#endif + Debug.Assert(objectID.GenerationNumber == item.Field3); + + //// Ignore the latter one. + if (!xrefTable.Contains(objectID)) + { +#if DEBUG + GetType(); +#endif + // Add iref for all uncrompressed objects. + xrefTable.Add(new PdfReference(objectID, position)); + + } + break; + + case 2: + // Nothing to do yet. + break; + } + } + } + return xrefStream; + } + + /// + /// Parses a PDF date string. + /// + internal static DateTime ParseDateTime(string date, DateTime errorValue) // TODO: TryParseDateTime + { + DateTime datetime = errorValue; + try + { + if (date.StartsWith("D:")) + { + // Format is + // D:YYYYMMDDHHmmSSOHH'mm' + // ^2 ^10 ^16 ^20 + int length = date.Length; + int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, hh = 0, mm = 0; + char o = 'Z'; + if (length >= 10) + { + year = Int32.Parse(date.Substring(2, 4)); + month = Int32.Parse(date.Substring(6, 2)); + day = Int32.Parse(date.Substring(8, 2)); + if (length >= 16) + { + hour = Int32.Parse(date.Substring(10, 2)); + minute = Int32.Parse(date.Substring(12, 2)); + second = Int32.Parse(date.Substring(14, 2)); + if (length >= 23) + { + if ((o = date[16]) != 'Z') + { + hh = Int32.Parse(date.Substring(17, 2)); + mm = Int32.Parse(date.Substring(20, 2)); + } + } + } + } + // There are miserable PDF tools around the world. + month = Math.Min(Math.Max(month, 1), 12); + datetime = new DateTime(year, month, day, hour, minute, second); + if (o != 'Z') + { + TimeSpan ts = new TimeSpan(hh, mm, 0); + if (o == '-') + datetime = datetime.Add(ts); + else + datetime = datetime.Subtract(ts); + } + // Now that we converted datetime to UTC, mark it as UTC. + DateTime.SpecifyKind(datetime, DateTimeKind.Utc); + } + else + { + // Some libraries use plain English format. + datetime = DateTime.Parse(date, CultureInfo.InvariantCulture); + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch (Exception ex) + { + // If we cannot parse datetime, just eat it, but give a hint in DEBUG build. + Debug.Assert(false, ex.Message); + } + return datetime; + } + + // /// + // /// Creates a parser for the specified PDF object type. A PDF object can define a specialized + // /// parser in the optional PdfObjectInfoAttribute. If no parser is specified, the default + // /// Parser object is returned. + // /// + // public static Parser CreateParser(PdfDocument document, Type pdfObjectType) + // { + // // TODO: ParserFactory + // object[] attribs = null; //pdfObjectType.GetCustomAttributes(typeof(PdfObjectInfoAttribute), false); + // if (attribs.Length == 1) + // { + // PdfObjectInfoAttribute attrib = null; //(PdfObjectInfoAttribute)attribs[0]; + // Type parserType = attrib.Parser; + // if (parserType != null) + // { + // ConstructorInfo ctorInfo = parserType.GetConstructor( + // BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, + // new Type[]{typeof(PdfDocument)}, null); + // Parser parser = (Parser)ctorInfo.Invoke(new object[]{document}); + // Debug.Assert(parser != null, "Creation of parser failed."); + // return parser; + // } + // } + // return new Parser(document); + // } + + /* + /// + /// Reads a date value directly or (optionally) indirectly from the PDF data stream. + /// + protected DateTime ReadDate(bool canBeIndirect) + { + Symbol symbol = lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.String || symbol == Symbol.UnicodeString || symbol == Symbol.HexString || symbol == Symbol.UnicodeHexString) + { + // D:YYYYMMDDHHmmSSOHH'mm' + // ^2 ^10 ^16 ^20 + string date = lexer.Token; + int length = date.Length; + int year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, hh = 0, mm = 0; + char o = 'Z'; + if (length >= 10) + { + year = Int32.Parse(date.Substring(2, 4)); + month = Int32.Parse(date.Substring(6, 2)); + day = Int32.Parse(date.Substring(8, 2)); + if (length >= 16) + { + hour = Int32.Parse(date.Substring(10, 2)); + minute = Int32.Parse(date.Substring(12, 2)); + second = Int32.Parse(date.Substring(14, 2)); + if (length >= 23) + { + if ((o = date[16]) != 'Z') + { + hh = Int32.Parse(date.Substring(17, 2)); + mm = Int32.Parse(date.Substring(20, 2)); + } + } + } + } + DateTime datetime = new DateTime(year, month, day, hour, minute, second); + if (o != 'Z') + { + TimeSpan ts = new TimeSpan(hh, mm, 0); + if (o == '+') + datetime.Add(ts); + else + datetime.Subtract(ts); + } + return datetime; + } + else if (symbol == Symbol.R) + { + int position = lexer.Position; + MoveToObject(lexer.Token); + ReadObjectID(null); + DateTime d = ReadDate(); + ReadSymbol(Symbol.EndObj); + lexer.Position = position; + return d; + } + thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + protected DateTime ReadDate() + { + return ReadDate(false); + } + + /// + /// Reads a PdfRectangle value directly or (optionally) indirectly from the PDF data stream. + /// + protected PdfRectangle ReadRectangle(bool canBeIndirect) + { + Symbol symbol = lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.BeginArray) + { + PdfRectangle rect = new PdfRectangle(); + rect.X1 = ReadReal(); + rect.Y1 = ReadReal(); + rect.X2 = ReadReal(); + rect.Y2 = ReadReal(); + ReadSymbol(Symbol.EndArray); + return rect; + } + else if (symbol == Symbol.R) + { + int position = lexer.Position; + MoveToObject(lexer.Token); + ReadObjectID(null); + PdfRectangle rect = ReadRectangle(); + ReadSymbol(Symbol.EndObj); + lexer.Position = position; + return rect; + } + thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + /// + /// Short cut for ReadRectangle(false). + /// + protected PdfRectangle ReadRectangle() + { + return ReadRectangle(false); + } + + /// + /// Reads a generic dictionary. + /// + protected PdfDictionary ReadDictionary(bool canBeIndirect) + { + // Just read over dictionary + PdfDictionary dictionary = new PdfDictionary(); + Symbol symbol = lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.BeginDictionary) + { + int nestingLevel = 0; + symbol = ScanNextToken(); + while (symbol != Symbol.Eof) + { + switch (symbol) + { + case Symbol.BeginDictionary: + nestingLevel++; + break; + + case Symbol.EndDictionary: + if (nestingLevel == 0) + return dictionary; + else + nestingLevel--; + break; + } + symbol = ScanNextToken(); + } + Debug.Assert(false, "Must not come here"); + return dictionary; + } + else if (symbol == Symbol.R) + { + return dictionary; + } + thr ow new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + /// + /// Short cut for ReadDictionary(false). + /// + protected PdfDictionary ReadDictionary() + { + return ReadDictionary(false); + } + + /// + /// Reads a generic array. + /// + protected PdfArray ReadArray(bool canBeIndirect) + { + // Just read over array + PdfArray array = new PdfArray(); + Symbol symbol = lexer.ScanNextToken(canBeIndirect); + if (symbol == Symbol.BeginArray) + { + int nestingLevel = 0; + symbol = ScanNextToken(); + while (symbol != Symbol.Eof) + { + switch (symbol) + { + case Symbol.BeginArray: + nestingLevel++; + break; + + case Symbol.EndArray: + if (nestingLevel == 0) + return array; + else + nestingLevel--; + break; + } + symbol = ScanNextToken(); + } + Debug.Assert(false, "Must not come here"); + return array; + } + else if (symbol == Symbol.R) + { + return array; + } + th row new PdfReaderException(PSSR.UnexpectedToken(lexer.Token)); + } + + protected PdfArray ReadArray() + { + return ReadArray(false); + } + + protected object ReadGeneric(KeysMeta meta, string token) + { + KeyDescriptor descriptor = meta[token]; + Debug.Assert(descriptor != null); + object result = null; + switch (descriptor.KeyType & KeyType.TypeMask) + { + case KeyType.Name: + result = ReadName(); + break; + + case KeyType.String: + result = ReadString(descriptor.CanBeIndirect); + break; + + case KeyType.Boolean: + result = ReadBoolean(descriptor.CanBeIndirect); + break; + + case KeyType.Integer: + result = ReadInteger(descriptor.CanBeIndirect); + break; + + case KeyType.Real: + result = ReadReal(descriptor.CanBeIndirect); + break; + + case KeyType.Date: + result = ReadDate(descriptor.CanBeIndirect); + break; + + case KeyType.Rectangle: + result = ReadRectangle(descriptor.CanBeIndirect); + break; + + case KeyType.Array: + result = ReadArray(descriptor.CanBeIndirect); + break; + + case KeyType.Dictionary: + result = ReadDictionary(descriptor.CanBeIndirect); + break; + + case KeyType.Stream: + break; + + case KeyType.NumberTree: + thr ow new NotImplementedException("KeyType.NumberTree"); + + case KeyType.NameOrArray: + char ch = lexer.MoveToNonWhiteSpace(); + if (ch == '/') + result = ReadName(); + else if (ch == '[') + result = ReadArray(); + else + th row new NotImplementedException("KeyType.NameOrArray"); + break; + + case KeyType.ArrayOrDictionary: + thr ow new NotImplementedException("KeyType.ArrayOrDictionary"); + } + //Debug.Assert(false, "ReadGeneric"); + return result; + } + + // /// + // /// Gets the current symbol from the lexer. + // /// + // protected Symbol Symbol + // { + // get {return lexer.Symbol;} + // } + // + // /// + // /// Gets the current token from the lexer. + // /// + // protected string Token + // { + // get {return lexer.Token.ToString();} + // } + + public static object Read(PdfObject o, string key) + { + return null; + } + */ + + private ParserState SaveState() + { + ParserState state = new ParserState(); + state.Position = _lexer.Position; + state.Symbol = _lexer.Symbol; + return state; + } + + private void RestoreState(ParserState state) + { + _lexer.Position = state.Position; + _lexer.Symbol = state.Symbol; + } + + private class ParserState + { + public int Position; + public Symbol Symbol; + } + + byte[] DecodeCrossReferenceStream(byte[] bytes, int columns, int predictor) + { + int size = bytes.Length; + if (predictor < 10 || predictor > 15) + throw new ArgumentException("Invalid predictor.", "predictor"); + + int rowSizeRaw = columns + 1; + + if (size % rowSizeRaw != 0) + throw new ArgumentException("Columns and size of array do not match."); + + int rows = size / rowSizeRaw; + + byte[] result = new byte[rows * columns]; +#if DEBUG + for (int i = 0; i < result.Length; ++i) + result[i] = 88; +#endif + + for (int row = 0; row < rows; ++row) + { + if (bytes[row * rowSizeRaw] != 2) + throw new ArgumentException("Invalid predictor in array."); + + for (int col = 0; col < columns; ++col) + { + // Copy data for first row. + if (row == 0) + result[row * columns + col] = bytes[row * rowSizeRaw + col + 1]; + else + { + // For other rows, add previous row. + result[row * columns + col] = (byte)(result[row * columns - columns + col] + bytes[row * rowSizeRaw + col + 1]); + } + } + } + return result; + } + + private readonly PdfDocument _document; + private readonly Lexer _lexer; + private readonly ShiftStack _stack; + } + + static class StreamHelper + { + public static int WSize(int[] w) + { + Debug.Assert(w.Length == 3); + return w[0] + w[1] + w[2]; + } + + public static uint ReadBytes(byte[] bytes, int index, int byteCount) + { + uint value = 0; + for (int idx = 0; idx < byteCount; idx++) + { + value *= 256; + value += bytes[index + idx]; + } + return value; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReader.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReader.cs new file mode 100644 index 00000000..08a9f965 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReader.cs @@ -0,0 +1,519 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using PdfSharp.Internal; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Security; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.IO +{ + /// + /// Encapsulates the arguments of the PdfPasswordProvider delegate. + /// + public class PdfPasswordProviderArgs + { + /// + /// Sets the password to open the document with. + /// + public string Password; + + /// + /// When set to true the PdfReader.Open function returns null indicating that no PdfDocument was created. + /// + public bool Abort; + } + + /// + /// A delegated used by the PdfReader.Open function to retrieve a password if the document is protected. + /// + public delegate void PdfPasswordProvider(PdfPasswordProviderArgs args); + + /// + /// Represents the functionality for reading PDF documents. + /// + public static class PdfReader + { + /// + /// Determines whether the file specified by its path is a PDF file by inspecting the first eight + /// bytes of the data. If the file header has the form %PDF-x.y the function returns the version + /// number as integer (e.g. 14 for PDF 1.4). If the file header is invalid or inaccessible + /// for any reason, 0 is returned. The function never throws an exception. + /// + public static int TestPdfFile(string path) + { +#if !NETFX_CORE + FileStream stream = null; + try + { + int pageNumber; + string realPath = Drawing.XPdfForm.ExtractPageNumber(path, out pageNumber); + if (File.Exists(realPath)) // prevent unwanted exceptions during debugging + { + stream = new FileStream(realPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + byte[] bytes = new byte[1024]; + stream.Read(bytes, 0, 1024); + return GetPdfFileVersion(bytes); + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch { } + finally + { + try + { + if (stream != null) + { +#if UWP + stream.Dispose(); +#else + stream.Close(); +#endif + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch + { + } + } +#endif + return 0; + } + + /// + /// Determines whether the specified stream is a PDF file by inspecting the first eight + /// bytes of the data. If the data begins with %PDF-x.y the function returns the version + /// number as integer (e.g. 14 for PDF 1.4). If the data is invalid or inaccessible + /// for any reason, 0 is returned. The function never throws an exception. + /// + public static int TestPdfFile(Stream stream) + { + long pos = -1; + try + { + pos = stream.Position; + byte[] bytes = new byte[1024]; + stream.Read(bytes, 0, 1024); + return GetPdfFileVersion(bytes); + } + // ReSharper disable once EmptyGeneralCatchClause + catch { } + finally + { + try + { + if (pos != -1) + stream.Position = pos; + } + // ReSharper disable once EmptyGeneralCatchClause + catch { } + } + return 0; + } + + /// + /// Determines whether the specified data is a PDF file by inspecting the first eight + /// bytes of the data. If the data begins with %PDF-x.y the function returns the version + /// number as integer (e.g. 14 for PDF 1.4). If the data is invalid or inaccessible + /// for any reason, 0 is returned. The function never throws an exception. + /// + public static int TestPdfFile(byte[] data) + { + return GetPdfFileVersion(data); + } + + /// + /// Implements scanning the PDF file version. + /// + internal static int GetPdfFileVersion(byte[] bytes) + { + try + { + // Acrobat accepts headers like %!PS-Adobe-N.n PDF-M.m... + string header = PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); // Encoding.ASCII.GetString(bytes); + if (header[0] == '%' || header.IndexOf("%PDF", StringComparison.Ordinal) >= 0) + { + int ich = header.IndexOf("PDF-", StringComparison.Ordinal); + if (ich > 0 && header[ich + 5] == '.') + { + char major = header[ich + 4]; + char minor = header[ich + 6]; + if (major >= '1' && major < '2' && minor >= '0' && minor <= '9') + return (major - '0') * 10 + (minor - '0'); + } + } + } + // ReSharper disable once EmptyGeneralCatchClause + catch { } + return 0; + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path, PdfDocumentOpenMode openmode) + { + return Open(path, null, openmode, null); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path, PdfDocumentOpenMode openmode, PdfPasswordProvider provider) + { + return Open(path, null, openmode, provider); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path, string password, PdfDocumentOpenMode openmode) + { + return Open(path, password, openmode, null); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path, string password, PdfDocumentOpenMode openmode, PdfPasswordProvider provider) + { +#if !NETFX_CORE + PdfDocument document; + Stream stream = null; + try + { + stream = new FileStream(path, FileMode.Open, FileAccess.Read); + document = Open(stream, password, openmode, provider); + if (document != null) + { + document._fullPath = Path.GetFullPath(path); + } + } + finally + { + if (stream != null) +#if !UWP + stream.Close(); +#else + stream.Dispose(); +#endif + } + return document; +#else + return null; +#endif + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path) + { + return Open(path, null, PdfDocumentOpenMode.Modify, null); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(string path, string password) + { + return Open(path, password, PdfDocumentOpenMode.Modify, null); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(Stream stream, PdfDocumentOpenMode openmode) + { + return Open(stream, null, openmode); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(Stream stream, PdfDocumentOpenMode openmode, PdfPasswordProvider passwordProvider) + { + return Open(stream, null, openmode, passwordProvider); + } + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMode openmode) + { + return Open(stream, password, openmode, null); + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMode openmode, PdfPasswordProvider passwordProvider) + { + PdfDocument document; + try + { + Lexer lexer = new Lexer(stream); + document = new PdfDocument(lexer); + document._state |= DocumentState.Imported; + document._openMode = openmode; + document._fileSize = stream.Length; + + // Get file version. + byte[] header = new byte[1024]; + stream.Position = 0; + stream.Read(header, 0, 1024); + document._version = GetPdfFileVersion(header); + if (document._version == 0) + throw new InvalidOperationException(PSSR.InvalidPdf); + + document._irefTable.IsUnderConstruction = true; + Parser parser = new Parser(document); + // Read all trailers or cross-reference streams, but no objects. + document._trailer = parser.ReadTrailer(); + if (document._trailer == null) + ParserDiagnostics.ThrowParserException("Invalid PDF file: no trailer found."); // TODO L10N using PSSR. + + Debug.Assert(document._irefTable.IsUnderConstruction); + document._irefTable.IsUnderConstruction = false; + + // Is document encrypted? + PdfReference xrefEncrypt = document._trailer.Elements[PdfTrailer.Keys.Encrypt] as PdfReference; + if (xrefEncrypt != null) + { + //xrefEncrypt.Value = parser.ReadObject(null, xrefEncrypt.ObjectID, false); + PdfObject encrypt = parser.ReadObject(null, xrefEncrypt.ObjectID, false, false); + + encrypt.Reference = xrefEncrypt; + xrefEncrypt.Value = encrypt; + PdfStandardSecurityHandler securityHandler = document.SecurityHandler; + TryAgain: + PasswordValidity validity = securityHandler.ValidatePassword(password); + if (validity == PasswordValidity.Invalid) + { + if (passwordProvider != null) + { + PdfPasswordProviderArgs args = new PdfPasswordProviderArgs(); + passwordProvider(args); + if (args.Abort) + return null; + password = args.Password; + goto TryAgain; + } + else + { + if (password == null) + throw new PdfReaderException(PSSR.PasswordRequired); + else + throw new PdfReaderException(PSSR.InvalidPassword); + } + } + else if (validity == PasswordValidity.UserPassword && openmode == PdfDocumentOpenMode.Modify) + { + if (passwordProvider != null) + { + PdfPasswordProviderArgs args = new PdfPasswordProviderArgs(); + passwordProvider(args); + if (args.Abort) + return null; + password = args.Password; + goto TryAgain; + } + else + throw new PdfReaderException(PSSR.OwnerPasswordRequired); + } + } + else + { + if (password != null) + { + // Password specified but document is not encrypted. + // ignore + } + } + + PdfReference[] irefs2 = document._irefTable.AllReferences; + int count2 = irefs2.Length; + + // 3rd: Create iRefs for all compressed objects. + Dictionary objectStreams = new Dictionary(); + for (int idx = 0; idx < count2; idx++) + { + PdfReference iref = irefs2[idx]; + PdfCrossReferenceStream xrefStream = iref.Value as PdfCrossReferenceStream; + if (xrefStream != null) + { + for (int idx2 = 0; idx2 < xrefStream.Entries.Count; idx2++) + { + PdfCrossReferenceStream.CrossReferenceStreamEntry item = xrefStream.Entries[idx2]; + // Is type xref to compressed object? + if (item.Type == 2) + { + //PdfReference irefNew = parser.ReadCompressedObject(new PdfObjectID((int)item.Field2), (int)item.Field3); + //document._irefTable.Add(irefNew); + int objectNumber = (int)item.Field2; + if (!objectStreams.ContainsKey(objectNumber)) + { + objectStreams.Add(objectNumber, null); + PdfObjectID objectID = new PdfObjectID((int)item.Field2); + parser.ReadIRefsFromCompressedObject(objectID); + } + } + } + } + } + + // 4th: Read compressed objects. + for (int idx = 0; idx < count2; idx++) + { + PdfReference iref = irefs2[idx]; + PdfCrossReferenceStream xrefStream = iref.Value as PdfCrossReferenceStream; + if (xrefStream != null) + { + for (int idx2 = 0; idx2 < xrefStream.Entries.Count; idx2++) + { + PdfCrossReferenceStream.CrossReferenceStreamEntry item = xrefStream.Entries[idx2]; + // Is type xref to compressed object? + if (item.Type == 2) + { + PdfReference irefNew = parser.ReadCompressedObject(new PdfObjectID((int)item.Field2), + (int)item.Field3); + Debug.Assert(document._irefTable.Contains(iref.ObjectID)); + //document._irefTable.Add(irefNew); + } + } + } + } + + + PdfReference[] irefs = document._irefTable.AllReferences; + int count = irefs.Length; + + // Read all indirect objects. + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; + if (iref.Value == null) + { +#if DEBUG_ + if (iref.ObjectNumber == 1074) + iref.GetType(); +#endif + try + { + Debug.Assert(document._irefTable.Contains(iref.ObjectID)); + PdfObject pdfObject = parser.ReadObject(null, iref.ObjectID, false, false); + Debug.Assert(pdfObject.Reference == iref); + pdfObject.Reference = iref; + Debug.Assert(pdfObject.Reference.Value != null, "Something went wrong."); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + // 4STLA rethrow exception to notify caller. + throw; + } + } + else + { + Debug.Assert(document._irefTable.Contains(iref.ObjectID)); + //iref.GetType(); + } + // Set maximum object number. + document._irefTable._maxObjectNumber = Math.Max(document._irefTable._maxObjectNumber, + iref.ObjectNumber); + } + + // Encrypt all objects. + if (xrefEncrypt != null) + { + document.SecurityHandler.EncryptDocument(); + } + + // Fix references of trailer values and then objects and irefs are consistent. + document._trailer.Finish(); + +#if DEBUG_ + // Some tests... + PdfReference[] reachables = document.xrefTable.TransitiveClosure(document.trailer); + reachables.GetType(); + reachables = document.xrefTable.AllXRefs; + document.xrefTable.CheckConsistence(); +#endif + + if (openmode == PdfDocumentOpenMode.Modify) + { + // Create new or change existing document IDs. + if (document.Internals.SecondDocumentID == "") + document._trailer.CreateNewDocumentIDs(); + else + { + byte[] agTemp = Guid.NewGuid().ToByteArray(); + document.Internals.SecondDocumentID = PdfEncoders.RawEncoding.GetString(agTemp, 0, agTemp.Length); + } + + // Change modification date + document.Info.ModificationDate = DateTime.Now; + + // Remove all unreachable objects + int removed = document._irefTable.Compact(); + if (removed != 0) + Debug.WriteLine("Number of deleted unreachable objects: " + removed); + + // Force flattening of page tree + PdfPages pages = document.Pages; + Debug.Assert(pages != null); + + //bool b = document.irefTable.Contains(new PdfObjectID(1108)); + //b.GetType(); + + document._irefTable.CheckConsistence(); + document._irefTable.Renumber(); + document._irefTable.CheckConsistence(); + } + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + throw; + } + return document; + } + + /// + /// Opens an existing PDF document. + /// + public static PdfDocument Open(Stream stream) + { + return Open(stream, PdfDocumentOpenMode.Modify); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReaderException.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReaderException.cs new file mode 100644 index 00000000..cbf19708 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfReaderException.cs @@ -0,0 +1,63 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.IO +{ + /// + /// Exception thrown by PdfReader. + /// + public class PdfReaderException : PdfSharpException + { + /// + /// Initializes a new instance of the class. + /// + public PdfReaderException() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + public PdfReaderException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The message. + /// The inner exception. + public PdfReaderException(string message, Exception innerException) + : + base(message, innerException) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfWriter.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfWriter.cs new file mode 100644 index 00000000..08071c5c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/PdfWriter.cs @@ -0,0 +1,661 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.IO; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Security; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf.IO +{ + /// + /// Represents a writer for generation of PDF streams. + /// + internal class PdfWriter + { + public PdfWriter(Stream pdfStream, PdfStandardSecurityHandler securityHandler) + { + _stream = pdfStream; + _securityHandler = securityHandler; + //System.Xml.XmlTextWriter +#if DEBUG + _layout = PdfWriterLayout.Verbose; +#endif + } + + public void Close(bool closeUnderlyingStream) + { + if (_stream != null && closeUnderlyingStream) + { +#if UWP + _stream.Dispose(); +#else + _stream.Close(); +#endif + } + _stream = null; + } + + public void Close() + { + Close(true); + } + + public int Position + { + get { return (int)_stream.Position; } + } + + /// + /// Gets or sets the kind of layout. + /// + public PdfWriterLayout Layout + { + get { return _layout; } + set { _layout = value; } + } + PdfWriterLayout _layout; + + public PdfWriterOptions Options + { + get { return _options; } + set { _options = value; } + } + PdfWriterOptions _options; + + // ----------------------------------------------------------- + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(bool value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value ? bool.TrueString : bool.FalseString); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfBoolean value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.Value ? "true" : "false"); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(int value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.ToString(CultureInfo.InvariantCulture)); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(uint value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.ToString(CultureInfo.InvariantCulture)); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfInteger value) + { + WriteSeparator(CharCat.Character); + _lastCat = CharCat.Character; + WriteRaw(value.Value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfUInteger value) + { + WriteSeparator(CharCat.Character); + _lastCat = CharCat.Character; + WriteRaw(value.Value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(double value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.ToString(Config.SignificantFigures7, CultureInfo.InvariantCulture)); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfReal value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.Value.ToString(Config.SignificantFigures7, CultureInfo.InvariantCulture)); + _lastCat = CharCat.Character; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfString value) + { + WriteSeparator(CharCat.Delimiter); +#if true + PdfStringEncoding encoding = (PdfStringEncoding)(value.Flags & PdfStringFlags.EncodingMask); + string pdf = (value.Flags & PdfStringFlags.HexLiteral) == 0 ? + PdfEncoders.ToStringLiteral(value.Value, encoding, SecurityHandler) : + PdfEncoders.ToHexStringLiteral(value.Value, encoding, SecurityHandler); + WriteRaw(pdf); +#else + switch (value.Flags & PdfStringFlags.EncodingMask) + { + case PdfStringFlags.Undefined: + case PdfStringFlags.PDFDocEncoding: + if ((value.Flags & PdfStringFlags.HexLiteral) == 0) + WriteRaw(PdfEncoders.DocEncode(value.Value, false)); + else + WriteRaw(PdfEncoders.DocEncodeHex(value.Value, false)); + break; + + case PdfStringFlags.WinAnsiEncoding: + throw new NotImplementedException("Unexpected encoding: WinAnsiEncoding"); + + case PdfStringFlags.Unicode: + if ((value.Flags & PdfStringFlags.HexLiteral) == 0) + WriteRaw(PdfEncoders.DocEncode(value.Value, true)); + else + WriteRaw(PdfEncoders.DocEncodeHex(value.Value, true)); + break; + + case PdfStringFlags.StandardEncoding: + case PdfStringFlags.MacRomanEncoding: + case PdfStringFlags.MacExpertEncoding: + default: + throw new NotImplementedException("Unexpected encoding"); + } +#endif + _lastCat = CharCat.Delimiter; + } + + /// + /// Writes the specified value to the PDF stream. + /// + public void Write(PdfName value) + { + WriteSeparator(CharCat.Delimiter, '/'); + string name = value.Value; + + StringBuilder pdf = new StringBuilder("/"); + for (int idx = 1; idx < name.Length; idx++) + { + char ch = name[idx]; + Debug.Assert(ch < 256); + if (ch > ' ') + switch (ch) + { + // TODO: is this all? + case '%': + case '/': + case '<': + case '>': + case '(': + case ')': + case '[': + case ']': + case '#': + break; + + default: + pdf.Append(name[idx]); + continue; + } + pdf.AppendFormat("#{0:X2}", (int)name[idx]); + } + WriteRaw(pdf.ToString()); + _lastCat = CharCat.Character; + } + + public void Write(PdfLiteral value) + { + WriteSeparator(CharCat.Character); + WriteRaw(value.Value); + _lastCat = CharCat.Character; + } + + public void Write(PdfRectangle rect) + { + const string format = Config.SignificantFigures3; + WriteSeparator(CharCat.Delimiter, '/'); + WriteRaw(PdfEncoders.Format("[{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "}]", rect.X1, rect.Y1, rect.X2, rect.Y2)); + _lastCat = CharCat.Delimiter; + } + + public void Write(PdfReference iref) + { + WriteSeparator(CharCat.Character); + WriteRaw(iref.ToString()); + _lastCat = CharCat.Character; + } + + public void WriteDocString(string text, bool unicode) + { + WriteSeparator(CharCat.Delimiter); + //WriteRaw(PdfEncoders.DocEncode(text, unicode)); + byte[] bytes; + if (!unicode) + bytes = PdfEncoders.DocEncoding.GetBytes(text); + else + bytes = PdfEncoders.UnicodeEncoding.GetBytes(text); + bytes = PdfEncoders.FormatStringLiteral(bytes, unicode, true, false, _securityHandler); + Write(bytes); + _lastCat = CharCat.Delimiter; + } + + public void WriteDocString(string text) + { + WriteSeparator(CharCat.Delimiter); + //WriteRaw(PdfEncoders.DocEncode(text, false)); + byte[] bytes = PdfEncoders.DocEncoding.GetBytes(text); + bytes = PdfEncoders.FormatStringLiteral(bytes, false, false, false, _securityHandler); + Write(bytes); + _lastCat = CharCat.Delimiter; + } + + public void WriteDocStringHex(string text) + { + WriteSeparator(CharCat.Delimiter); + //WriteRaw(PdfEncoders.DocEncodeHex(text)); + byte[] bytes = PdfEncoders.DocEncoding.GetBytes(text); + bytes = PdfEncoders.FormatStringLiteral(bytes, false, false, true, _securityHandler); + _stream.Write(bytes, 0, bytes.Length); + _lastCat = CharCat.Delimiter; + } + + /// + /// Begins a direct or indirect dictionary or array. + /// + public void WriteBeginObject(PdfObject obj) + { + bool indirect = obj.IsIndirect; + if (indirect) + { + WriteObjectAddress(obj); + if (_securityHandler != null) + _securityHandler.SetHashKey(obj.ObjectID); + } + _stack.Add(new StackItem(obj)); + if (indirect) + { + if (obj is PdfArray) + WriteRaw("[\n"); + else if (obj is PdfDictionary) + WriteRaw("<<\n"); + _lastCat = CharCat.NewLine; + } + else + { + if (obj is PdfArray) + { + WriteSeparator(CharCat.Delimiter); + WriteRaw('['); + _lastCat = CharCat.Delimiter; + } + else if (obj is PdfDictionary) + { + NewLine(); + WriteSeparator(CharCat.Delimiter); + WriteRaw("<<\n"); + _lastCat = CharCat.NewLine; + } + } + if (_layout == PdfWriterLayout.Verbose) + IncreaseIndent(); + } + + /// + /// Ends a direct or indirect dictionary or array. + /// + public void WriteEndObject() + { + int count = _stack.Count; + Debug.Assert(count > 0, "PdfWriter stack underflow."); + + StackItem stackItem = _stack[count - 1]; + _stack.RemoveAt(count - 1); + + PdfObject value = stackItem.Object; + bool indirect = value.IsIndirect; + if (_layout == PdfWriterLayout.Verbose) + DecreaseIndent(); + if (value is PdfArray) + { + if (indirect) + { + WriteRaw("\n]\n"); + _lastCat = CharCat.NewLine; + } + else + { + WriteRaw("]"); + _lastCat = CharCat.Delimiter; + } + } + else if (value is PdfDictionary) + { + if (indirect) + { + if (!stackItem.HasStream) + WriteRaw(_lastCat == CharCat.NewLine ? ">>\n" : " >>\n"); + } + else + { + Debug.Assert(!stackItem.HasStream, "Direct object with stream??"); + WriteSeparator(CharCat.NewLine); + WriteRaw(">>\n"); + _lastCat = CharCat.NewLine; + } + } + if (indirect) + { + NewLine(); + WriteRaw("endobj\n"); + if (_layout == PdfWriterLayout.Verbose) + WriteRaw("%--------------------------------------------------------------------------------------------------\n"); + } + } + + /// + /// Writes the stream of the specified dictionary. + /// + public void WriteStream(PdfDictionary value, bool omitStream) + { + StackItem stackItem = _stack[_stack.Count - 1]; + Debug.Assert(stackItem.Object is PdfDictionary); + Debug.Assert(stackItem.Object.IsIndirect); + stackItem.HasStream = true; + + WriteRaw(_lastCat == CharCat.NewLine ? ">>\nstream\n" : " >>\nstream\n"); + + if (omitStream) + { + WriteRaw(" ...stream content omitted...\n"); // useful for debugging only + } + else + { + byte[] bytes = value.Stream.Value; + if (bytes.Length != 0) + { + if (_securityHandler != null) + { + bytes = (byte[])bytes.Clone(); + bytes = _securityHandler.EncryptBytes(bytes); + } + Write(bytes); + if (_lastCat != CharCat.NewLine) + WriteRaw('\n'); + } + } + WriteRaw("endstream\n"); + } + + public void WriteRaw(string rawString) + { + if (String.IsNullOrEmpty(rawString)) + return; + + byte[] bytes = PdfEncoders.RawEncoding.GetBytes(rawString); + _stream.Write(bytes, 0, bytes.Length); + _lastCat = GetCategory((char)bytes[bytes.Length - 1]); + } + + public void WriteRaw(char ch) + { + Debug.Assert(ch < 256, "Raw character greater than 255 detected."); + + _stream.WriteByte((byte)ch); + _lastCat = GetCategory(ch); + } + + public void Write(byte[] bytes) + { + if (bytes == null || bytes.Length == 0) + return; + + _stream.Write(bytes, 0, bytes.Length); + _lastCat = GetCategory((char)bytes[bytes.Length - 1]); + } + + void WriteObjectAddress(PdfObject value) + { + if (_layout == PdfWriterLayout.Verbose) + WriteRaw(String.Format("{0} {1} obj % {2}\n", + value.ObjectID.ObjectNumber, value.ObjectID.GenerationNumber, + value.GetType().FullName)); + else + WriteRaw(String.Format("{0} {1} obj\n", value.ObjectID.ObjectNumber, value.ObjectID.GenerationNumber)); + } + + public void WriteFileHeader(PdfDocument document) + { + StringBuilder header = new StringBuilder("%PDF-"); + int version = document._version; + header.Append((version / 10).ToString(CultureInfo.InvariantCulture) + "." + + (version % 10).ToString(CultureInfo.InvariantCulture) + "\n%\xD3\xF4\xCC\xE1\n"); + WriteRaw(header.ToString()); + + if (_layout == PdfWriterLayout.Verbose) + { + WriteRaw(String.Format("% PDFsharp Version {0} (verbose mode)\n", VersionInfo.Version)); + // Keep some space for later fix-up. + _commentPosition = (int)_stream.Position + 2; + WriteRaw("% \n"); + WriteRaw("% \n"); + WriteRaw("% \n"); + WriteRaw("% \n"); + WriteRaw("% \n"); + WriteRaw("%--------------------------------------------------------------------------------------------------\n"); + } + } + + public void WriteEof(PdfDocument document, int startxref) + { + WriteRaw("startxref\n"); + WriteRaw(startxref.ToString(CultureInfo.InvariantCulture)); + WriteRaw("\n%%EOF\n"); + int fileSize = (int)_stream.Position; + if (_layout == PdfWriterLayout.Verbose) + { + TimeSpan duration = DateTime.Now - document._creation; + + _stream.Position = _commentPosition; + // Without InvariantCulture parameter the following line fails if the current culture is e.g. + // a Far East culture, because the date string contains non-ASCII characters. + // So never never never never use ToString without a culture info. + WriteRaw("Creation date: " + document._creation.ToString("G", CultureInfo.InvariantCulture)); + _stream.Position = _commentPosition + 50; + WriteRaw("Creation time: " + duration.TotalSeconds.ToString("0.000", CultureInfo.InvariantCulture) + " seconds"); + _stream.Position = _commentPosition + 100; + WriteRaw("File size: " + fileSize.ToString(CultureInfo.InvariantCulture) + " bytes"); + _stream.Position = _commentPosition + 150; + WriteRaw("Pages: " + document.Pages.Count.ToString(CultureInfo.InvariantCulture)); + _stream.Position = _commentPosition + 200; + WriteRaw("Objects: " + document._irefTable.ObjectTable.Count.ToString(CultureInfo.InvariantCulture)); + } + } + + /// + /// Gets or sets the indentation for a new indentation level. + /// + internal int Indent + { + get { return _indent; } + set { _indent = value; } + } + int _indent = 2; + int _writeIndent; + + /// + /// Increases indent level. + /// + void IncreaseIndent() + { + _writeIndent += _indent; + } + + /// + /// Decreases indent level. + /// + void DecreaseIndent() + { + _writeIndent -= _indent; + } + + ///// + ///// Returns an indent string of blanks. + ///// + //static string Ind(int _indent) + //{ + // return new String(' ', _indent); + //} + + /// + /// Gets an indent string of current indent. + /// + string IndentBlanks + { + get { return new string(' ', _writeIndent); } + } + + void WriteIndent() + { + WriteRaw(IndentBlanks); + } + + void WriteSeparator(CharCat cat, char ch) + { + switch (_lastCat) + { + case CharCat.NewLine: + if (_layout == PdfWriterLayout.Verbose) + WriteIndent(); + break; + + case CharCat.Delimiter: + break; + + case CharCat.Character: + if (_layout == PdfWriterLayout.Verbose) + { + //if (cat == CharCat.Character || ch == '/') + _stream.WriteByte((byte)' '); + } + else + { + if (cat == CharCat.Character) + _stream.WriteByte((byte)' '); + } + break; + } + } + + void WriteSeparator(CharCat cat) + { + WriteSeparator(cat, '\0'); + } + + public void NewLine() + { + if (_lastCat != CharCat.NewLine) + WriteRaw('\n'); + } + + CharCat GetCategory(char ch) + { + if (Lexer.IsDelimiter(ch)) + return CharCat.Delimiter; + if (ch == Chars.LF) + return CharCat.NewLine; + return CharCat.Character; + } + + enum CharCat + { + NewLine, + Character, + Delimiter, + } + CharCat _lastCat; + + /// + /// Gets the underlying stream. + /// + internal Stream Stream + { + get { return _stream; } + } + Stream _stream; + + internal PdfStandardSecurityHandler SecurityHandler + { + get { return _securityHandler; } + set { _securityHandler = value; } + } + PdfStandardSecurityHandler _securityHandler; + + class StackItem + { + public StackItem(PdfObject value) + { + Object = value; + } + + public readonly PdfObject Object; + public bool HasStream; + } + + readonly List _stack = new List(); + int _commentPosition; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/ShiftStack.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/ShiftStack.cs new file mode 100644 index 00000000..8d842e02 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/ShiftStack.cs @@ -0,0 +1,142 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace PdfSharp.Pdf.IO +{ + /// + /// Represents the stack for the shift-reduce parser. It seems that it is only needed for + /// reduction of indirect references. + /// + internal class ShiftStack + { + // TODO: make Lexer.PeekChars(20) and scan for 'R' to detect indirect references + + public ShiftStack() + { + _items = new List(); + } + + public PdfItem[] ToArray(int start, int length) + { + PdfItem[] items = new PdfItem[length]; + for (int i = 0, j = start; i < length; i++, j++) + items[i] = _items[j]; + return items; + } + + /// + /// Gets the stack pointer index. + /// + // ReSharper disable InconsistentNaming + public int SP + // ReSharper restore InconsistentNaming + { + get { return _sp; } + } + + /// + /// Gets the value at the specified index. Valid index is in range 0 up to sp-1. + /// + public PdfItem this[int index] + { + get + { + if (index >= _sp) + throw new ArgumentOutOfRangeException("index", index, "Value greater than stack index."); + return _items[index]; + } + } + + /// + /// Gets an item relative to the current stack pointer. The index must be a negative value (-1, -2, etc.). + /// + public PdfItem GetItem(int relativeIndex) + { + if (relativeIndex >= 0 || -relativeIndex > _sp) + throw new ArgumentOutOfRangeException("relativeIndex", relativeIndex, "Value out of stack range."); + return _items[_sp + relativeIndex]; + } + + /// + /// Gets an item relative to the current stack pointer. The index must be a negative value (-1, -2, etc.). + /// + public int GetInteger(int relativeIndex) + { + if (relativeIndex >= 0 || -relativeIndex > _sp) + throw new ArgumentOutOfRangeException("relativeIndex", relativeIndex, "Value out of stack range."); + return ((PdfInteger)_items[_sp + relativeIndex]).Value; + } + + /// + /// Pushes the specified item onto the stack. + /// + public void Shift(PdfItem item) + { + Debug.Assert(item != null); + _items.Add(item); + _sp++; + } + + /// + /// Replaces the last 'count' items with the specified item. + /// + public void Reduce(int count) + { + if (count > _sp) + throw new ArgumentException("count causes stack underflow."); + _items.RemoveRange(_sp - count, count); + _sp -= count; + } + + /// + /// Replaces the last 'count' items with the specified item. + /// + public void Reduce(PdfItem item, int count) + { + Debug.Assert(item != null); + Reduce(count); + _items.Add(item); + _sp++; + } + + /// + /// The stack pointer index. Points to the next free item. + /// + int _sp; + + /// + /// An array representing the stack. + /// + readonly List _items; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PasswordValidity.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PasswordValidity.cs new file mode 100644 index 00000000..49331a0c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PasswordValidity.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.IO +{ + /// + /// Determines the type of the password. + /// + public enum PasswordValidity + { + /// + /// Password is neither user nor owner password. + /// + Invalid, + + /// + /// Password is user password. + /// + UserPassword, + + /// + /// Password is owner password. + /// + OwnerPassword, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfDocumentOpenMode.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfDocumentOpenMode.cs new file mode 100644 index 00000000..52fd13b0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfDocumentOpenMode.cs @@ -0,0 +1,63 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.IO +{ + /// + /// Determines how a PDF document is opened. + /// + public enum PdfDocumentOpenMode + { + /// + /// The PDF stream is completely read into memory and can be modified. Pages can be deleted or + /// inserted, but it is not possible to extract pages. This mode is useful for modifying an + /// existing PDF document. + /// + Modify, + + /// + /// The PDF stream is opened for importing pages from it. A document opened in this mode cannot + /// be modified. + /// + Import, + + /// + /// The PDF stream is completely read into memory, but cannot be modified. This mode preserves the + /// original internal structure of the document and is useful for analyzing existing PDF files. + /// + ReadOnly, + + /// + /// The PDF stream is partially read for information purposes only. The only valid operation is to + /// call the Info property at the imported document. This option is very fast and needs less memory + /// and is e.g. useful for browsing information about a collection of PDF documents in a user interface. + /// + InformationOnly, // TODO: not yet implemented + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterLayout.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterLayout.cs new file mode 100644 index 00000000..65cd16e4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterLayout.cs @@ -0,0 +1,61 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.IO +{ + /// + /// Determines how the PDF output stream is formatted. Even all formats create valid PDF files, + /// only Compact or Standard should be used for production purposes. + /// + public enum PdfWriterLayout + { + /// + /// The PDF stream contains no unnecessary characters. This is default in release build. + /// + Compact, + + /// + /// The PDF stream contains some superfluous line feeds, but is more readable. + /// + Standard, + + /// + /// The PDF stream is indented to reflect the nesting levels of the objects. This is useful + /// for analyzing PDF files, but increases the size of the file significantly. + /// + Indented, + + /// + /// The PDF stream is indented to reflect the nesting levels of the objects and contains additional + /// information about the PDFsharp objects. Furthermore content streams are not deflated. This + /// is useful for debugging purposes only and increases the size of the file significantly. + /// + Verbose, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterOptions.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterOptions.cs new file mode 100644 index 00000000..3d9996c3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/PdfWriterOptions.cs @@ -0,0 +1,56 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.IO +{ + /// + /// INTERNAL USE ONLY. + /// + [Flags] + internal enum PdfWriterOptions + { + /// + /// If only this flag is specified the result is a regular valid PDF stream. + /// + Regular = 0x000000, + + /// + /// Omit writing stream data. For debugging purposes only. + /// With this option the result is not valid PDF. + /// + OmitStream = 0x000001, + + /// + /// Omit inflate filter. For debugging purposes only. + /// + OmitInflation = 0x000002, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/Symbol.cs b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/Symbol.cs new file mode 100644 index 00000000..ce23504b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.IO/enums/Symbol.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.IO +{ + /// + /// Terminal symbols recognized by lexer. + /// + public enum Symbol + { +#pragma warning disable 1591 + None, + Comment, Null, Integer, UInteger, Real, Boolean, String, HexString, UnicodeString, UnicodeHexString, + Name, Keyword, + BeginStream, EndStream, + BeginArray, EndArray, + BeginDictionary, EndDictionary, + Obj, EndObj, R, XRef, Trailer, StartXRef, Eof, +#pragma warning restore 1591 + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/AnsiEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/AnsiEncoding.cs new file mode 100644 index 00000000..79b6f69d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/AnsiEncoding.cs @@ -0,0 +1,292 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Text; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// An encoder for PDF AnsiEncoding. + /// + public sealed class AnsiEncoding : Encoding + { +#if DEBUG_ && !(SILVERLIGHT || NETFX_CORE) + public static void ProofImplementation() + { + // Implementation was verified with .NET Ansi encoding. + Encoding dotnetImplementation = Encoding.GetEncoding(1252); + Encoding thisImplementation = new AnsiEncoding(); + + // Check ANSI chars. + for (int i = 0; i <= 255; i++) + { + byte[] b = { (byte) i }; + char[] ch1 = dotnetImplementation.GetChars(b, 0, 1); + char[] ch2 = thisImplementation.GetChars(b, 0, 1); + if (ch1[0] != ch2[0]) + Debug.Print("Error"); + byte[] b1 = dotnetImplementation.GetBytes(ch1, 0, 1); + byte[] b2 = thisImplementation.GetBytes(ch1, 0, 1); + if (b1.Length != b2.Length || b1.Length > 1 || b1[0] != b2[0]) + Debug.Print("Error"); + } + + // Check Unicode chars. + for (int i = 0; i <= 65535; i++) + { + if (i >= 256) + break; + if (i == 0x80) + Debug.Print(""); + char[] ch = new char[] { (char)i }; + byte[] b1 = dotnetImplementation.GetBytes(ch, 0, 1); + byte[] b2 = thisImplementation.GetBytes(ch, 0, 1); + if (b1.Length != b2.Length || b1.Length > 1 || b1[0] != b2[0]) + Debug.Print("Error"); + //byte[] b = new byte[] { (byte)i }; + //char ch = (char)i; + char[] ch1 = dotnetImplementation.GetChars(b1, 0, 1); + char[] ch2 = thisImplementation.GetChars(b2, 0, 1); + if (ch1[0] != ch2[0]) + Debug.Print("Error"); + } + } +#endif + + /// + /// Gets the byte count. + /// + public override int GetByteCount(char[] chars, int index, int count) + { + return count; + } + + /// + /// Gets the bytes. + /// + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + int count = charCount; + for (; charCount > 0; byteIndex++, charIndex++, charCount--) + bytes[byteIndex] = (byte)UnicodeToAnsi(chars[charIndex]); + return count; + } + + /// + /// Gets the character count. + /// + public override int GetCharCount(byte[] bytes, int index, int count) + { + return count; + } + + /// + /// Gets the chars. + /// + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int idx = byteCount; idx > 0; byteIndex++, charIndex++, idx--) + chars[charIndex] = AnsiToUnicode[bytes[byteIndex]]; + return byteCount; + } + + /// + /// When overridden in a derived class, calculates the maximum number of bytes produced by encoding the specified number of characters. + /// + /// The number of characters to encode. + /// + /// The maximum number of bytes produced by encoding the specified number of characters. + /// + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + /// + /// When overridden in a derived class, calculates the maximum number of characters produced by decoding the specified number of bytes. + /// + /// The number of bytes to decode. + /// + /// The maximum number of characters produced by decoding the specified number of bytes. + /// + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + + /// + /// Indicates whether the specified Unicode character is available in the ANSI code page 1252. + /// + public static bool IsAnsi1252Char(char ch) + { + if (ch < '\u0080' || (ch >= '\u00A0' && ch <= '\u00FF')) + return true; + + switch (ch) + { + case '\u20AC': + case '\u0081': + case '\u201A': + case '\u0192': + case '\u201E': + case '\u2026': + case '\u2020': + case '\u2021': + case '\u02C6': + case '\u2030': + case '\u0160': + case '\u2039': + case '\u0152': + case '\u008D': + case '\u017D': + case '\u008F': + case '\u0090': + case '\u2018': + case '\u2019': + case '\u201C': + case '\u201D': + case '\u2022': + case '\u2013': + case '\u2014': + case '\u02DC': + case '\u2122': + case '\u0161': + case '\u203A': + case '\u0153': + case '\u009D': + case '\u017E': + case '\u0178': + return true; + } + return false; + } + + /// + /// Maps Unicode to ANSI code page 1252. + /// + public static char UnicodeToAnsi(char ch) + { + if (ch < '\u0080' || (ch >= '\u00A0' && ch <= '\u00FF')) + return ch; + + switch (ch) + { + case '\u20AC': + return '\u0080'; + case '\u0081': + return '\u0081'; + case '\u201A': + return '\u0082'; + case '\u0192': + return '\u0083'; + case '\u201E': + return '\u0084'; + case '\u2026': + return '\u0085'; + case '\u2020': + return '\u0086'; + case '\u2021': + return '\u0087'; + case '\u02C6': + return '\u0088'; + case '\u2030': + return '\u0089'; + case '\u0160': + return '\u008A'; + case '\u2039': + return '\u008B'; + case '\u0152': + return '\u008C'; + case '\u008D': + return '\u008D'; + case '\u017D': + return '\u008E'; + case '\u008F': + return '\u008F'; + case '\u0090': + return '\u0090'; + case '\u2018': + return '\u0091'; + case '\u2019': + return '\u0092'; + case '\u201C': + return '\u0093'; + case '\u201D': + return '\u0094'; + case '\u2022': + return '\u0095'; + case '\u2013': + return '\u0096'; + case '\u2014': + return '\u0097'; + case '\u02DC': + return '\u0098'; + case '\u2122': + return '\u0099'; + case '\u0161': + return '\u009A'; + case '\u203A': + return '\u009B'; + case '\u0153': + return '\u009C'; + case '\u009D': + return '\u009D'; + case '\u017E': + return '\u009E'; + case '\u0178': + return '\u009F'; + } + return '\u00A4'; // Char 164 is ANSI value of ''. + } + + /// + /// Maps WinAnsi to Unicode characters. + /// + static readonly char[] AnsiToUnicode = // new char[/*256*/] + { + // 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + /* 00 */ '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', '\u0008', '\u0009', '\u000A', '\u000B', '\u000C', '\u000D', '\u000E', '\u000F', + /* 10 */ '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', '\u0016', '\u0017', '\u0018', '\u0019', '\u001A', '\u001B', '\u001C', '\u001D', '\u001E', '\u001F', + /* 20 */ '\u0020', '\u0021', '\u0022', '\u0023', '\u0024', '\u0025', '\u0026', '\u0027', '\u0028', '\u0029', '\u002A', '\u002B', '\u002C', '\u002D', '\u002E', '\u002F', + /* 30 */ '\u0030', '\u0031', '\u0032', '\u0033', '\u0034', '\u0035', '\u0036', '\u0037', '\u0038', '\u0039', '\u003A', '\u003B', '\u003C', '\u003D', '\u003E', '\u003F', + /* 40 */ '\u0040', '\u0041', '\u0042', '\u0043', '\u0044', '\u0045', '\u0046', '\u0047', '\u0048', '\u0049', '\u004A', '\u004B', '\u004C', '\u004D', '\u004E', '\u004F', + /* 50 */ '\u0050', '\u0051', '\u0052', '\u0053', '\u0054', '\u0055', '\u0056', '\u0057', '\u0058', '\u0059', '\u005A', '\u005B', '\u005C', '\u005D', '\u005E', '\u005F', + /* 60 */ '\u0060', '\u0061', '\u0062', '\u0063', '\u0064', '\u0065', '\u0066', '\u0067', '\u0068', '\u0069', '\u006A', '\u006B', '\u006C', '\u006D', '\u006E', '\u006F', + /* 70 */ '\u0070', '\u0071', '\u0072', '\u0073', '\u0074', '\u0075', '\u0076', '\u0077', '\u0078', '\u0079', '\u007A', '\u007B', '\u007C', '\u007D', '\u007E', '\u007F', + /* 80 */ '\u20AC', '\u0081', '\u201A', '\u0192', '\u201E', '\u2026', '\u2020', '\u2021', '\u02C6', '\u2030', '\u0160', '\u2039', '\u0152', '\u008D', '\u017D', '\u008F', + /* 90 */ '\u0090', '\u2018', '\u2019', '\u201C', '\u201D', '\u2022', '\u2013', '\u2014', '\u02DC', '\u2122', '\u0161', '\u203A', '\u0153', '\u009D', '\u017E', '\u0178', + /* A0 */ '\u00A0', '\u00A1', '\u00A2', '\u00A3', '\u00A4', '\u00A5', '\u00A6', '\u00A7', '\u00A8', '\u00A9', '\u00AA', '\u00AB', '\u00AC', '\u00AD', '\u00AE', '\u00AF', + /* B0 */ '\u00B0', '\u00B1', '\u00B2', '\u00B3', '\u00B4', '\u00B5', '\u00B6', '\u00B7', '\u00B8', '\u00B9', '\u00BA', '\u00BB', '\u00BC', '\u00BD', '\u00BE', '\u00BF', + /* C0 */ '\u00C0', '\u00C1', '\u00C2', '\u00C3', '\u00C4', '\u00C5', '\u00C6', '\u00C7', '\u00C8', '\u00C9', '\u00CA', '\u00CB', '\u00CC', '\u00CD', '\u00CE', '\u00CF', + /* D0 */ '\u00D0', '\u00D1', '\u00D2', '\u00D3', '\u00D4', '\u00D5', '\u00D6', '\u00D7', '\u00D8', '\u00D9', '\u00DA', '\u00DB', '\u00DC', '\u00DD', '\u00DE', '\u00DF', + /* E0 */ '\u00E0', '\u00E1', '\u00E2', '\u00E3', '\u00E4', '\u00E5', '\u00E6', '\u00E7', '\u00E8', '\u00E9', '\u00EA', '\u00EB', '\u00EC', '\u00ED', '\u00EE', '\u00EF', + /* F0 */ '\u00F0', '\u00F1', '\u00F2', '\u00F3', '\u00F4', '\u00F5', '\u00F6', '\u00F7', '\u00F8', '\u00F9', '\u00FA', '\u00FB', '\u00FC', '\u00FD', '\u00FE', '\u00FF' + }; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/ColorSpaceHelper.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/ColorSpaceHelper.cs new file mode 100644 index 00000000..f3133cb7 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/ColorSpaceHelper.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// Helper functions for RGB and CMYK colors. + /// + static class ColorSpaceHelper + { + /// + /// Checks whether a color mode and a color match. + /// + public static XColor EnsureColorMode(PdfColorMode colorMode, XColor color) + { +#if true + if (colorMode == PdfColorMode.Rgb && color.ColorSpace != XColorSpace.Rgb) + return XColor.FromArgb((int)(color.A * 255), color.R, color.G, color.B); + + if (colorMode == PdfColorMode.Cmyk && color.ColorSpace != XColorSpace.Cmyk) + return XColor.FromCmyk(color.A, color.C, color.M, color.Y, color.K); + + return color; +#else + if (colorMode == PdfColorMode.Rgb && color.ColorSpace != XColorSpace.Rgb) + throw new InvalidOperationException(PSSR.InappropriateColorSpace(colorMode, color.ColorSpace)); + + if (colorMode == PdfColorMode.Cmyk && color.ColorSpace != XColorSpace.Cmyk) + throw new InvalidOperationException(PSSR.InappropriateColorSpace(colorMode, color.ColorSpace)); +#endif + } + + /// + /// Checks whether the color mode of a document and a color match. + /// + public static XColor EnsureColorMode(PdfDocument document, XColor color) + { + if (document == null) + throw new ArgumentNullException("document"); + + return EnsureColorMode(document.Options.ColorMode, color); + } + + /// + /// Determines whether two colors are equal referring to their CMYK color values. + /// + public static bool IsEqualCmyk(XColor x, XColor y) + { + if (x.ColorSpace != XColorSpace.Cmyk || y.ColorSpace != XColorSpace.Cmyk) + return false; + return x.C == y.C && x.M == y.M && x.Y == y.Y && x.K == y.K; + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/DocEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/DocEncoding.cs new file mode 100644 index 00000000..19d87997 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/DocEncoding.cs @@ -0,0 +1,147 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Text; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// An encoder for PDF DocEncoding. + /// + internal sealed class DocEncoding : Encoding + { + public DocEncoding() + { } + + public override int GetByteCount(char[] chars, int index, int count) + { + return PdfEncoders.WinAnsiEncoding.GetByteCount(chars, index, count); + } + + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + byte[] ansi = PdfEncoders.WinAnsiEncoding.GetBytes(chars, charIndex, charCount); + for (int idx = 0, count = ansi.Length; count > 0; idx++, byteIndex++, count--) + bytes[byteIndex] = AnsiToDoc[ansi[idx]]; + return ansi.Length; + } + + public override int GetCharCount(byte[] bytes, int index, int count) + { + //return PdfEncoders.WinAnsiEncoding.GetCharCount(bytes, index, count); + Debug.Assert(PdfEncoders.WinAnsiEncoding.GetCharCount(bytes, index, count) == count); + return count; + } + + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + PdfDocToUnicode.GetType(); + throw new NotImplementedException("GetChars"); + //for (; byteCount > 0; byteIndex++, charIndex++, byteCount--) + // chars[charIndex] = (char)bytes[byteIndex]; + //return byteCount; + } + + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + + /// + /// Converts WinAnsi to DocEncode characters. Based upon PDF Reference 1.6. + /// + static readonly byte[] AnsiToDoc = new byte[256] + { + // x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf + /* 00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 10 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + /* 20 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + /* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + /* 60 */ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + /* 70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + /* 80 */ 0xA0, 0x81, 0x91, 0x83, 0x8C, 0x83, 0x81, 0x82, 0x1A, 0x89, 0x97, 0x88, 0x96, 0x8D, 0x99, 0x8F, + /* 90 */ 0x90, 0x8F, 0x90, 0x8D, 0x8E, 0x80, 0x85, 0x84, 0x1F, 0x92, 0x90, 0x89, 0x9C, 0x9D, 0x9E, 0x98, + /* a0 */ 0x20, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /* b0 */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + /* c0 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + /* d0 */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /* e0 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + /* f0 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + + ////// Converts WinAnsi to DocEncode characters. Incomplete, just maps € and some other characters. + ////// x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf + /////* 00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /////* 10 */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + /////* 20 */ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + /////* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + /////* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + /////* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + /////* 60 */ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + /////* 70 */ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + /////* 80 */ 0xA0,#0x7F, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + /////* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,#0x8A,#0x8C, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + /////* a0 */ 0x20, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + /////* b0 */ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + /////* c0 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + /////* d0 */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /////* e0 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + /////* f0 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + }; + + // TODO: use this table + static readonly char[] PdfDocToUnicode = new char[] + { + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', + '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', + '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', '\x2F', + '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39', '\x3A', '\x3B', '\x3C', '\x3D', '\x3E', '\x3F', + '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', '\x48', '\x49', '\x4A', '\x4B', '\x4C', '\x4D', '\x4E', '\x4F', + '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', '\x58', '\x59', '\x5A', '\x5B', '\x5C', '\x5D', '\x5E', '\x5F', + '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F', + '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F', + '\x2022', '\x2020', '\x2021', '\x2026', '\x2014', '\x2013', '\x0192', '\x2044', '\x2039', '\x203A', '\x2212', '\x2030', '\x201E', '\x201C', '\x201D', '\x2018', + '\x2019', '\x201A', '\x2122', '\xFB01', '\xFB02', '\x0141', '\x0152', '\x0160', '\x0178', '\x017D', '\x0131', '\x0142', '\x0153', '\x0161', '\x017E', '\xFFFD', + '\x20AC', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7', '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF', + '\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7', '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF', + '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF', + '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF', + '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF', + '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', '\xFF', + }; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/GlobalObjectTable.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/GlobalObjectTable.cs new file mode 100644 index 00000000..4f917f39 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/GlobalObjectTable.cs @@ -0,0 +1,125 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace PdfSharp.Pdf.Internal +{ +#if true_ + /// + /// Provides a thread-local cache for large objects. + /// + internal class GlobalObjectTable_not_in_use + { + public GlobalObjectTable_not_in_use() + { } + + public void AttatchDocument(PdfDocument.DocumentHandle handle) + { + lo ck (_documentHandles) + { + _documentHandles.Add(handle); + } + } + + public void DetatchDocument(PdfDocument.DocumentHandle handle) + { + lo ck (_documentHandles) + { + // Notify other documents about detach + int count = _documentHandles.Count; + for (int idx = 0; idx < count; idx++) + { + if (((PdfDocument.DocumentHandle)_documentHandles[idx]).IsAlive) + { + PdfDocument target = ((PdfDocument.DocumentHandle)_documentHandles[idx]).Target; + if (target != null) + target.OnExternalDocumentFinalized(handle); + } + } + + // Clean up table + for (int idx = 0; idx < _documentHandles.Count; idx++) + { + PdfDocument target = ((PdfDocument.DocumentHandle)_documentHandles[idx]).Target; + if (target == null) + { + _documentHandles.RemoveAt(idx); + idx--; + } + } + } + + //lo ck (documents) + //{ + // int index = IndexOf(document); + // if (index != -1) + // { + // documents.RemoveAt(index); + // int count = documents.Count; + // for (int idx = 0; idx < count; idx++) + // { + // PdfDocument target = ((WeakReference)documents[idx]).Target as PdfDocument; + // if (target != null) + // target.OnExternalDocumentFinalized(document); + // } + + // for (int idx = 0; idx < documents.Count; idx++) + // { + // PdfDocument target = ((WeakReference)documents[idx]).Target as PdfDocument; + // if (target == null) + // { + // documents.RemoveAt(idx); + // idx--; + // } + // } + // } + //} + } + + //int IndexOf(PdfDocument.Handle handle) + //{ + // int count = documents.Count; + // for (int idx = 0; idx < count; idx++) + // { + // if ((PdfDocument.Handle)documents[idx] == handle) + // return idx; + // //if (Object.ReferenceEquals(((WeakReference)documents[idx]).Target, document)) + // // return idx; + // } + // return -1; + //} + + /// + /// Array of handles to all documents. + /// + readonly List _documentHandles = new List(); + } +#endif +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfDiagnostics.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfDiagnostics.cs new file mode 100644 index 00000000..51746e65 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfDiagnostics.cs @@ -0,0 +1,55 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Internal +{ + class PdfDiagnostics + { + public static bool TraceCompressedObjects + { + get { return _traceCompressedObjects; } + set { _traceCompressedObjects = value; } + } + static bool _traceCompressedObjects = true; + + public static bool TraceXrefStreams + { + get { return _traceXrefStreams && TraceCompressedObjects; } + set { _traceXrefStreams = value; } + } + static bool _traceXrefStreams = true; + + public static bool TraceObjectStreams + { + get { return _traceObjectStreams && TraceCompressedObjects; } + set { _traceObjectStreams = value; } + } + static bool _traceObjectStreams = true; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfEncoders.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfEncoders.cs new file mode 100644 index 00000000..661d4dc6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/PdfEncoders.cs @@ -0,0 +1,662 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using PdfSharp.Drawing; +using PdfSharp.Pdf.Security; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// Groups a set of static encoding helper functions. + /// + internal static class PdfEncoders + { + /// + /// Gets the raw encoding. + /// + public static Encoding RawEncoding + { + get { return _rawEncoding ?? (_rawEncoding = new RawEncoding()); } + } + static Encoding _rawEncoding; + + /// + /// Gets the raw Unicode encoding. + /// + public static Encoding RawUnicodeEncoding + { + get { return _rawUnicodeEncoding ?? (_rawUnicodeEncoding = new RawUnicodeEncoding()); } + } + static Encoding _rawUnicodeEncoding; + + /// + /// Gets the Windows 1252 (ANSI) encoding. + /// + public static Encoding WinAnsiEncoding + { + get + { + if (_winAnsiEncoding == null) + { +#if !SILVERLIGHT && !NETFX_CORE && !UWP + // Use .net encoder if available. + _winAnsiEncoding = Encoding.GetEncoding(1252); +#else + // Use own implementation in Silverlight and WinRT + _winAnsiEncoding = new AnsiEncoding(); +#endif + } + return _winAnsiEncoding; + } + } + static Encoding _winAnsiEncoding; + + /// + /// Gets the PDF DocEncoding encoding. + /// + public static Encoding DocEncoding + { + get { return _docEncoding ?? (_docEncoding = new DocEncoding()); } + } + static Encoding _docEncoding; + + /// + /// Gets the UNICODE little-endian encoding. + /// + public static Encoding UnicodeEncoding + { + get { return _unicodeEncoding ?? (_unicodeEncoding = Encoding.Unicode); } + } + static Encoding _unicodeEncoding; + + ///// + ///// Encodes a string from a byte array. Each character gets the code of the corresponding byte. + ///// + //public static string RawString(byte[] bytes, int offset, int length) + //{ + // char[] chars = new char[length]; + // for (int idx = offset, ch = 0; idx < offset + length; idx++, ch++) + // chars[ch] = (char)bytes[idx]; + // return new string(chars, 0, length); + //} + // + //public static string RawString(byte[] bytes) + //{ + // return RawString(bytes, 0, bytes.Length); + //} + +#if true_ + public static string EncodeAsLiteral(string text, bool unicode) + { + if (text == null || text == "") + return "<>"; + + StringBuilder pdf = new StringBuilder(""); + if (!unicode) + { + byte[] bytes = WinAnsiEncoding.GetBytes(text); + int count = bytes.Length; + pdf.Append("("); + for (int idx = 0; idx < count; idx++) + { + char ch = (char)bytes[idx]; + if (ch < 32) + { + switch (ch) + { + case '\n': + pdf.Append("\\n"); + break; + + case '\r': + pdf.Append("\\r"); + break; + + case '\t': + pdf.Append("\\t"); + break; + + case '\f': + pdf.Append("\\f"); + break; + + default: + pdf.Append(InvalidChar); // TODO + break; + } + } + else + { + switch (ch) + { + case '(': + pdf.Append("\\("); + break; + + case ')': + pdf.Append("\\)"); + break; + + case '\\': + pdf.Append("\\\\"); + break; + + default: + pdf.Append(ch); + break; + } + } + } + pdf.Append(')'); + } + else + { + pdf.Append("<"); + byte[] bytes = UnicodeEncoding.GetBytes(text); + int count = bytes.Length; + for (int idx = 0; idx < count; idx += 2) + { + pdf.AppendFormat("{0:X2}{1:X2}", bytes[idx + 1], bytes[idx]); + if (idx != 0 && (idx % 48) == 0) + pdf.Append("\n"); + } + pdf.Append(">"); + } + return pdf.ToString(); + } +#endif + + //public static string EncodeAsLiteral(string text) + //{ + // return EncodeAsLiteral(text, false); + //} + + /// + /// Converts a raw string into a raw string literal, possibly encrypted. + /// + public static string ToStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler) + { + if (String.IsNullOrEmpty(text)) + return "()"; + + byte[] bytes; + switch (encoding) + { + case PdfStringEncoding.RawEncoding: + bytes = RawEncoding.GetBytes(text); + break; + + case PdfStringEncoding.WinAnsiEncoding: + bytes = WinAnsiEncoding.GetBytes(text); + break; + + case PdfStringEncoding.PDFDocEncoding: + bytes = DocEncoding.GetBytes(text); + break; + + case PdfStringEncoding.Unicode: + bytes = RawUnicodeEncoding.GetBytes(text); + break; + + default: + throw new NotImplementedException(encoding.ToString()); + } + byte[] temp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, false, securityHandler); + return RawEncoding.GetString(temp, 0, temp.Length); + } + + /// + /// Converts a raw string into a raw string literal, possibly encrypted. + /// + public static string ToStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler) + { + if (bytes == null || bytes.Length == 0) + return "()"; + + byte[] temp = FormatStringLiteral(bytes, unicode, true, false, securityHandler); + return RawEncoding.GetString(temp, 0, temp.Length); + } + + /// + /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted. + /// + public static string ToHexStringLiteral(string text, PdfStringEncoding encoding, PdfStandardSecurityHandler securityHandler) + { + if (String.IsNullOrEmpty(text)) + return "<>"; + + byte[] bytes; + switch (encoding) + { + case PdfStringEncoding.RawEncoding: + bytes = RawEncoding.GetBytes(text); + break; + + case PdfStringEncoding.WinAnsiEncoding: + bytes = WinAnsiEncoding.GetBytes(text); + break; + + case PdfStringEncoding.PDFDocEncoding: + bytes = DocEncoding.GetBytes(text); + break; + + case PdfStringEncoding.Unicode: + //bytes = UnicodeEncoding.GetBytes(text); + bytes = RawUnicodeEncoding.GetBytes(text); + break; + + default: + throw new NotImplementedException(encoding.ToString()); + } + + byte[] agTemp = FormatStringLiteral(bytes, encoding == PdfStringEncoding.Unicode, true, true, securityHandler); + return RawEncoding.GetString(agTemp, 0, agTemp.Length); + } + + /// + /// Converts a raw string into a raw hexadecimal string literal, possibly encrypted. + /// + public static string ToHexStringLiteral(byte[] bytes, bool unicode, PdfStandardSecurityHandler securityHandler) + { + if (bytes == null || bytes.Length == 0) + return "<>"; + + byte[] agTemp = FormatStringLiteral(bytes, unicode, true, true, securityHandler); + return RawEncoding.GetString(agTemp, 0, agTemp.Length); + } + + /// + /// Converts the specified byte array into a byte array representing a string literal. + /// + /// The bytes of the string. + /// Indicates whether one or two bytes are one character. + /// Indicates whether to use Unicode prefix. + /// Indicates whether to create a hexadecimal string literal. + /// Encrypts the bytes if specified. + /// The PDF bytes. + public static byte[] FormatStringLiteral(byte[] bytes, bool unicode, bool prefix, bool hex, PdfStandardSecurityHandler securityHandler) + { + if (bytes == null || bytes.Length == 0) + return hex ? new byte[] { (byte)'<', (byte)'>' } : new byte[] { (byte)'(', (byte)')' }; + + Debug.Assert(!unicode || bytes.Length % 2 == 0, "Odd number of bytes in Unicode string."); + + byte[] originalBytes = null; + + bool encrypted = false; + if (securityHandler != null && !hex) + { + originalBytes = bytes; + bytes = (byte[])bytes.Clone(); + bytes = securityHandler.EncryptBytes(bytes); + encrypted = true; + } + + int count = bytes.Length; + StringBuilder pdf = new StringBuilder(); + if (!unicode) + { + if (!hex) + { + pdf.Append("("); + for (int idx = 0; idx < count; idx++) + { + char ch = (char)bytes[idx]; + if (ch < 32) + { + switch (ch) + { + case '\n': + pdf.Append("\\n"); + break; + + case '\r': + pdf.Append("\\r"); + break; + + case '\t': + pdf.Append("\\t"); + break; + + case '\b': + pdf.Append("\\b"); + break; + + // Corrupts encrypted text. + //case '\f': + // pdf.Append("\\f"); + // break; + + default: + // Don't escape characters less than 32 if the string is encrypted, because it is + // unreadable anyway. + encrypted = true; + if (!encrypted) + { + pdf.Append("\\0"); + pdf.Append((char)(ch % 8 + '0')); + pdf.Append((char)(ch / 8 + '0')); + } + else + pdf.Append(ch); + break; + } + } + else + { + switch (ch) + { + case '(': + pdf.Append("\\("); + break; + + case ')': + pdf.Append("\\)"); + break; + + case '\\': + pdf.Append("\\\\"); + break; + + default: + pdf.Append(ch); + break; + } + } + } + pdf.Append(')'); + } + else + { + pdf.Append('<'); + for (int idx = 0; idx < count; idx++) + pdf.AppendFormat("{0:X2}", bytes[idx]); + pdf.Append('>'); + } + } + else + { + //Hex: + if (hex) + { + if (securityHandler != null && prefix) + { + // TODO Reduce redundancy. + // Encrypt data after padding BOM. + var bytes2 = new byte[bytes.Length + 2]; + // Add BOM. + bytes2[0] = 0xfe; + bytes2[1] = 0xff; + // Copy bytes. + Array.Copy(bytes, 0, bytes2, 2, bytes.Length); + // Encyption. + bytes2 = securityHandler.EncryptBytes(bytes2); + encrypted = true; + pdf.Append("<"); + var count2 = bytes2.Length; + for (int idx = 0; idx < count2; idx += 2) + { + pdf.AppendFormat("{0:X2}{1:X2}", bytes2[idx], bytes2[idx + 1]); + if (idx != 0 && (idx % 48) == 0) + pdf.Append("\n"); + } + pdf.Append(">"); + } + else + { + // No prefix or no encryption. + pdf.Append(prefix ? ""); + } + } + else + { + // TODO non hex literals... not sure how to treat linefeeds, '(', '\' etc. + if (encrypted) + { + // Hack: Call self with hex := true. + return FormatStringLiteral(originalBytes, unicode, prefix, true, securityHandler); + } + else + { + // Hack: Call self with hex := true. + return FormatStringLiteral(bytes, true, prefix, true, null); + } + } + } + return RawEncoding.GetBytes(pdf.ToString()); + } + + /// + /// Converts WinAnsi to DocEncode characters. Incomplete, just maps and some other characters. + /// + static byte[] docencode_______ = new byte[256] + { + // TODO: + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0xA0, 0x7F, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x8A, 0x8C, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + }; + + //public static string DocEncode(string text, bool unicode)//, PdfStandardSecurityHandler securityHandler) + //{ + // if (text == null || text == "") + // return "()"; + // + // int length = text.Length; + // StringBuilder encoded = new StringBuilder(2 * length); + // if (!unicode) + // { + // byte[] bytes = WinAnsiEncoding.GetBytes(text); + // encoded.Append('('); + // for (int idx = 0; idx < length; idx++) + // { + // char ch = (char)bytes[idx]; + // if (ch > 255) + // { + // //TODO unicode? + // encoded.Append(InvalidChar); + // //encoded.Append(ch); + // continue; + // } + // ch = (char)docencode[(int)ch]; + // if (ch < 32) + // { + // switch (ch) + // { + // case '\n': + // encoded.Append("\\n"); + // break; + // + // case '\r': + // encoded.Append("\\r"); + // break; + // + // case '\t': + // encoded.Append("\\t"); + // break; + // + // case '\f': + // encoded.Append("\\f"); + // break; + // + // default: + // encoded.Append(InvalidChar); // TODO + // break; + // } + // } + // else + // { + // switch (ch) + // { + // case '(': + // encoded.Append("\\("); + // break; + // + // case ')': + // encoded.Append("\\)"); + // break; + // + // case '\\': + // encoded.Append("\\\\"); + // break; + // + // default: + // encoded.Append(ch); + // break; + // } + // } + // } + // encoded.Append(')'); + // } + // else + // { + // encoded.Append("'); + // } + // return encoded.ToString(); + //} + + //public static string DocEncode(string text) + //{ + // return DocEncode(text, false); + //} + + ///// + ///// Encodes a hexadecimal doc-encoded string literal. + ///// + //public static string DocEncodeHex(string text, bool unicode) + //{ + // if (text == null || text == "") + // return "<>"; + // + // int length = text.Length; + // StringBuilder encoded = new StringBuilder(3 * length); + // if (!unicode) + // { + // byte[] bytes = WinAnsiEncoding.GetBytes(text); + // encoded.Append('<'); + // for (int idx = 0; idx < length; idx++) + // encoded.AppendFormat("{0:X2}", docencode[bytes[idx]]); + // encoded.Append('>'); + // } + // else + // { + // encoded.Append("'); + // } + // return encoded.ToString(); + //} + + //public static string DocEncodeHex(string text) + //{ + // return DocEncodeHex(text, false); + //} + + /// + /// ...because I always forget CultureInfo.InvariantCulture and wonder why Acrobat + /// cannot understand my German decimal separator... + /// + public static string Format(string format, params object[] args) + { + return String.Format(CultureInfo.InvariantCulture, format, args); + } + + /// + /// Converts a float into a string with up to 3 decimal digits and a decimal point. + /// + public static string ToString(double val) + { + return val.ToString(Config.SignificantFigures3, CultureInfo.InvariantCulture); + } + + /// + /// Converts an XColor into a string with up to 3 decimal digits and a decimal point. + /// + public static string ToString(XColor color, PdfColorMode colorMode) + { + const string format = Config.SignificantFigures3; + + // If not defined let color decide + if (colorMode == PdfColorMode.Undefined) + colorMode = color.ColorSpace == XColorSpace.Cmyk ? PdfColorMode.Cmyk : PdfColorMode.Rgb; + + switch (colorMode) + { + case PdfColorMode.Cmyk: + return String.Format(CultureInfo.InvariantCulture, "{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "}", + color.C, color.M, color.Y, color.K); + + default: + return String.Format(CultureInfo.InvariantCulture, "{0:" + format + "} {1:" + format + "} {2:" + format + "}", + color.R / 255.0, color.G / 255.0, color.B / 255.0); + } + } + + /// + /// Converts an XMatrix into a string with up to 4 decimal digits and a decimal point. + /// + public static string ToString(XMatrix matrix) + { + const string format = Config.SignificantFigures4; + return String.Format(CultureInfo.InvariantCulture, + "{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "} {4:" + format + "} {5:" + format + "}", + matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.OffsetX, matrix.OffsetY); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawEncoding.cs new file mode 100644 index 00000000..f85b008b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawEncoding.cs @@ -0,0 +1,165 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Text; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// An encoder for raw strings. The raw encoding is simply the identity relation between + /// characters and bytes. PDFsharp internally works with raw encoded strings instead of + /// byte arrays because strings are much more handy than byte arrays. + /// + /// + /// Raw encoded strings represent an array of bytes. Therefore a character greater than + /// 255 is not valid in a raw encoded string. + /// + public sealed class RawEncoding : Encoding + { + /// + /// Initializes a new instance of the class. + /// + // ReSharper disable EmptyConstructor + public RawEncoding() + { } + // ReSharper restore EmptyConstructor + + /// + /// When overridden in a derived class, calculates the number of bytes produced by encoding a set of characters from the specified character array. + /// + /// The character array containing the set of characters to encode. + /// The index of the first character to encode. + /// The number of characters to encode. + /// + /// The number of bytes produced by encoding the specified characters. + /// + public override int GetByteCount(char[] chars, int index, int count) + { + return count; + } + + /// + /// When overridden in a derived class, encodes a set of characters from the specified character array into the specified byte array. + /// + /// The character array containing the set of characters to encode. + /// The index of the first character to encode. + /// The number of characters to encode. + /// The byte array to contain the resulting sequence of bytes. + /// The index at which to start writing the resulting sequence of bytes. + /// + /// The actual number of bytes written into . + /// + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + for (int count = charCount; count > 0; charIndex++, byteIndex++, count--) + { +#if DEBUG_ + if ((uint) chars[charIndex] > 255) + Debug-Break.Break(true); +#endif + //Debug.Assert((uint)chars[charIndex] < 256, "Raw string contains invalid character with a value > 255."); + bytes[byteIndex] = (byte)chars[charIndex]; + //#warning Here is a HACK that must not be ignored! + // HACK: + // bytes[byteIndex] = (byte)chars[charIndex]; + } + return charCount; + } + + /// + /// When overridden in a derived class, calculates the number of characters produced by decoding a sequence of bytes from the specified byte array. + /// + /// The byte array containing the sequence of bytes to decode. + /// The index of the first byte to decode. + /// The number of bytes to decode. + /// + /// The number of characters produced by decoding the specified sequence of bytes. + /// + public override int GetCharCount(byte[] bytes, int index, int count) + { + return count; + } + + /// + /// When overridden in a derived class, decodes a sequence of bytes from the specified byte array into the specified character array. + /// + /// The byte array containing the sequence of bytes to decode. + /// The index of the first byte to decode. + /// The number of bytes to decode. + /// The character array to contain the resulting set of characters. + /// The index at which to start writing the resulting set of characters. + /// + /// The actual number of characters written into . + /// + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int count = byteCount; count > 0; byteIndex++, charIndex++, count--) + chars[charIndex] = (char)bytes[byteIndex]; + return byteCount; + } + + /// + /// When overridden in a derived class, calculates the maximum number of bytes produced by encoding the specified number of characters. + /// + /// The number of characters to encode. + /// + /// The maximum number of bytes produced by encoding the specified number of characters. + /// + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + /// + /// When overridden in a derived class, calculates the maximum number of characters produced by decoding the specified number of bytes. + /// + /// The number of bytes to decode. + /// + /// The maximum number of characters produced by decoding the specified number of bytes. + /// + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + +#if SILVERLIGHT + /// + /// When overridden in a derived class, decodes all the bytes in the specified byte array into a string. + /// + /// The byte array containing the sequence of bytes to decode. + /// + /// A containing the results of decoding the specified sequence of bytes. + /// + public string GetString(byte[] bytes) + { + return GetString(bytes, 0, bytes.Length); + } +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawUnicodeEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawUnicodeEncoding.cs new file mode 100644 index 00000000..0682ac44 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/RawUnicodeEncoding.cs @@ -0,0 +1,84 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Text; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// An encoder for Unicode strings. + /// (That means, a character represents a glyph index.) + /// + internal sealed class RawUnicodeEncoding : Encoding + { + public RawUnicodeEncoding() + { } + + public override int GetByteCount(char[] chars, int index, int count) + { + // Each character represents exactly an ushort value, which is a glyph index. + return 2 * count; + } + + public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) + { + for (int count = charCount; count > 0; charIndex++, count--) + { + char ch = chars[charIndex]; + bytes[byteIndex++] = (byte)(ch >> 8); + bytes[byteIndex++] = (byte)ch; + } + return charCount * 2; + } + + public override int GetCharCount(byte[] bytes, int index, int count) + { + return count / 2; + } + + public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) + { + for (int count = byteCount; count > 0; byteIndex += 2, charIndex++, count--) + { + chars[charIndex] = (char)((int)bytes[byteIndex] << 8 + (int)bytes[byteIndex + 1]); + } + return byteCount; + } + + public override int GetMaxByteCount(int charCount) + { + return charCount * 2; + } + + public override int GetMaxCharCount(int byteCount) + { + return byteCount / 2; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Internal/ThreadLocalStorage.cs b/src/PDFsharp/src/PdfSharp/Pdf.Internal/ThreadLocalStorage.cs new file mode 100644 index 00000000..3cbd03ee --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Internal/ThreadLocalStorage.cs @@ -0,0 +1,128 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf.Internal +{ + /// + /// Provides a thread-local cache for large objects. + /// + internal class ThreadLocalStorage // #??? + { + public ThreadLocalStorage() + { + _importedDocuments = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public void AddDocument(string path, PdfDocument document) + { + _importedDocuments.Add(path, document.Handle); + } + + public void RemoveDocument(string path) + { + _importedDocuments.Remove(path); + } + + public PdfDocument GetDocument(string path) + { + Debug.Assert(path.StartsWith("*") || Path.IsPathRooted(path), "Path must be full qualified."); + + PdfDocument document = null; + PdfDocument.DocumentHandle handle; + if (_importedDocuments.TryGetValue(path, out handle)) + { + document = handle.Target; + if (document == null) + RemoveDocument(path); + } + if (document == null) + { + document = PdfReader.Open(path, PdfDocumentOpenMode.Import); + _importedDocuments.Add(path, document.Handle); + } + return document; + } + + public PdfDocument[] Documents + { + get + { + List list = new List(); + foreach (PdfDocument.DocumentHandle handle in _importedDocuments.Values) + { + if (handle.IsAlive) + list.Add(handle.Target); + } + return list.ToArray(); + } + } + + public void DetachDocument(PdfDocument.DocumentHandle handle) + { + if (handle.IsAlive) + { + foreach (string path in _importedDocuments.Keys) + { + if (_importedDocuments[path] == handle) + { + _importedDocuments.Remove(path); + break; + } + } + } + + // Clean table + bool itemRemoved = true; + while (itemRemoved) + { + itemRemoved = false; + foreach (string path in _importedDocuments.Keys) + { + if (!_importedDocuments[path].IsAlive) + { + _importedDocuments.Remove(path); + itemRemoved = true; + break; + } + } + } + } + + /// + /// Maps path to document handle. + /// + readonly Dictionary _importedDocuments; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Printing/PdfFilePrinter.cs b/src/PDFsharp/src/PdfSharp/Pdf.Printing/PdfFilePrinter.cs new file mode 100644 index 00000000..62caaaeb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Printing/PdfFilePrinter.cs @@ -0,0 +1,254 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Removed + +#if true_ +namespace PdfSharp.Pdf.Printing +{ + // Some googled inforamtion about command line switches: + // + // AcroRd32.exe filename Executes the reader and displays a file. + // AcroRd32.exe /p filename Executes the reader and prints a file. + // AcroRd32.exe /t path printername drivername portname Executes the reader and prints a file + // while suppressing the Acrobat print + // dialog box, then terminating the Reader. + // + // The four parameters of the /t option evaluate to strings. + // printername The name of the Printer. + // drivername Your printer drivers name i.e. whatever apperars in the Driver Used box when viewing printer properties. + // portname The printers port. portname cannot contain any "/" characters; if it does, output is routed to + // the default port for that printer. + // + // + // Acrobat.exe /n Launch a separate instance of the Acrobat application + // Acrobat.exe /s Open Acrobat suppressing the splash screen + // Acrobat.exe /o Open Acrobat suppressing the openfile dialog + // Acrobat.exe /h Open Acrobat in hidden mode + + + /// + /// A wrapper around Adobe Reader or Adobe Acrobat that helps to print PDF files. + /// The property AdobeReaderPath must be set before the class can be used for printing. + /// The class was tested with Adobe Reader 7.0.7. + /// If this stuff does not work, please don't write me mails! + /// If you enhance this class, please let me know. + /// + public class PdfFilePrinter + { + /// + /// Initializes a new instance of the class. + /// + public PdfFilePrinter() + { } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the PDF file. + public PdfFilePrinter(string pdfFileName) + { + PdfFileName = pdfFileName; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the PDF file. + /// Name of the printer. + public PdfFilePrinter(string pdfFileName, string printerName) + { + _pdfFileName = pdfFileName; + _printerName = printerName; + } + + /// + /// Gets or sets the name of the PDF file to print. + /// + public string PdfFileName + { + get { return _pdfFileName; } + set { _pdfFileName = value; } + } + string _pdfFileName; + + /// + /// Gets or sets the name of the printer. A typical name looks like '\\myserver\HP LaserJet PCL5'. + /// + /// The name of the printer. + public string PrinterName + { + get { return _printerName; } + set { _printerName = value; } + } + string _printerName; + + /// + /// Gets or sets the working directory. + /// + public string WorkingDirectory + { + get { return _workingDirectory; } + set { _workingDirectory = value; } + } + string _workingDirectory; + + /// + /// Prints the PDF file. + /// + public void Print() + { + Print(-1); + } + + /// + /// Prints the PDF file. + /// + /// The number of milliseconds to wait for completing the print job. + public void Print(int milliseconds) + { + if (String.IsNullOrEmpty(_printerName)) + _printerName = _defaultPrinterName; + + if (String.IsNullOrEmpty(_adobeReaderPath)) + throw new InvalidOperationException("No full qualified path to AcroRd32.exe or Acrobat.exe is set."); + + if (String.IsNullOrEmpty(_printerName)) + throw new InvalidOperationException("No printer name set."); + + // Check whether file exists. + string fqName; + if (!string.IsNullOrEmpty(_workingDirectory)) + fqName = Path.Combine(_workingDirectory, _pdfFileName); + else + fqName = Path.Combine(Directory.GetCurrentDirectory(), _pdfFileName); + if (!File.Exists(fqName)) + throw new InvalidOperationException(String.Format("The file {0} does not exist.", fqName)); + + // TODO: Check whether printer exists. + + try + { + DoSomeVeryDirtyHacksToMakeItWork(); + + //acrord32.exe /t <- seems to work best + //acrord32.exe /h /p <- some swear by this version + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName = _adobeReaderPath; + string args = String.Format("/t \"{0}\" \"{1}\"", _pdfFileName, _printerName); + //Debug.WriteLine(args); + startInfo.Arguments = args; + startInfo.CreateNoWindow = true; + startInfo.ErrorDialog = false; + startInfo.UseShellExecute = false; + if (!String.IsNullOrEmpty(_workingDirectory)) + startInfo.WorkingDirectory = _workingDirectory; + + Process process = Process.Start(startInfo); + if (!process.WaitForExit(milliseconds)) + { + // Kill Adobe Reader/Acrobat + process.Kill(); + } + } + catch (Exception ex) + { + // ReSharper disable PossibleIntendedRethrow + throw ex; + // ReSharper restore PossibleIntendedRethrow + } + } + + /// + /// For reasons only Adobe knows the Reader seams to open and shows the document instead of printing it + /// when it was not already running. + /// If you use PDFsharp and have any suggestions to circumvent this function, please let us know. + /// + void DoSomeVeryDirtyHacksToMakeItWork() + { + if (_runningAcro != null) + { + if (!_runningAcro.HasExited) + return; + _runningAcro.Dispose(); + _runningAcro = null; + } + // Is any Adobe Reader/Acrobat running? + Process[] processes = Process.GetProcesses(); + int count = processes.Length; + for (int idx = 0; idx < count; idx++) + { + try + { + Process process = processes[idx]; + ProcessModule module = process.MainModule; + + if (String.Compare(Path.GetFileName(module.FileName), Path.GetFileName(_adobeReaderPath), StringComparison.OrdinalIgnoreCase) == 0) + { + // Yes: Fine, we can print. + _runningAcro = process; + break; + } + } + catch { } + } + if (_runningAcro == null) + { + // No: Start an Adobe Reader/Acrobat. + // If you are within ASP.NET, good luck... + _runningAcro = Process.Start(_adobeReaderPath); + if (_runningAcro != null) + _runningAcro.WaitForInputIdle(); + } + } + static Process _runningAcro; + + /// + /// Gets or sets the Adobe Reader or Adobe Acrobat path. + /// A typical name looks like 'C:\Program Files\Adobe\Adobe Reader 7.0\AcroRd32.exe'. + /// + static public string AdobeReaderPath + { + get { return _adobeReaderPath; } + set { _adobeReaderPath = value; } + } + static string _adobeReaderPath; + + /// + /// Gets or sets the name of the default printer. A typical name looks like '\\myserver\HP LaserJet PCL5'. + /// + static public string DefaultPrinterName + { + get { return _defaultPrinterName; } + set { _defaultPrinterName = value; } + } + static string _defaultPrinterName; + } +} +#endif \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/MD5Managed.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/MD5Managed.cs new file mode 100644 index 00000000..a99fe8ea --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/MD5Managed.cs @@ -0,0 +1,426 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Based on code from here: +// http://archive.msdn.microsoft.com/SilverlightMD5/Release/ProjectReleases.aspx?ReleaseId=2206 +// +// ************************************************************** +// * Raw implementation of the MD5 hash algorithm +// * from RFC 1321. +// * +// * Written By: Reid Borsuk and Jenny Zheng +// * Copyright (c) Microsoft Corporation. All rights reserved. +// ************************************************************** + +using System; +using System.Diagnostics; +#if !NETFX_CORE && !UWP +using System.Security.Cryptography; +#endif + +// ReSharper disable InconsistentNaming + +#if SILVERLIGHT || WINDOWS_PHONE || UWP || (GDI && DEBUG) +namespace PdfSharp.Pdf.Security +{ +#if UWP + class HashAlgorithm + { + public int HashSizeValue { get; set; } + + public virtual void Initialize() + { } + + protected virtual void HashCore(byte[] array, int ibStart, int cbSize) + { } + + protected virtual byte[] HashFinal() + { + return null; + } + + public byte[] HashValue { get; set; } + + public void TransformBlock(byte[] a, int b, int c, byte[] d, int e) + { } + + public void TransformFinalBlock(byte[] a, int b, int c) + { } + + public byte[] ComputeHash(byte[] a) + { + return null; + } + + public byte[] Hash + { + get { return null; } + } + } +#endif + /// + /// A managed implementation of the MD5 algorithm. + /// Necessary because MD5 is not part of the framework in Silverlight and WP. + /// + class MD5Managed + //#if !UWP + : HashAlgorithm // TODO: WinRT has not even a HashAlgorithm base class. + //#endif + { + // Intitial values as defined in RFC 1321. + const uint A = 0x67452301; + const uint B = 0xefcdab89; + const uint C = 0x98badcfe; + const uint D = 0x10325476; + + public MD5Managed() + { + HashSizeValue = 128; + Initialize(); + } + + public sealed override void Initialize() + { + _data = new byte[64]; + _dataSize = 0; + _totalLength = 0; + _abcd = new MD5Core.ABCDStruct(); + + // Intitial values as defined in RFC 1321. + _abcd.A = A; + _abcd.B = B; + _abcd.C = C; + _abcd.D = D; + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + int startIndex = ibStart; + int totalArrayLength = _dataSize + cbSize; + if (totalArrayLength >= 64) + { + Array.Copy(array, startIndex, _data, _dataSize, 64 - _dataSize); + // Process message of 64 bytes (512 bits) + MD5Core.GetHashBlock(_data, ref _abcd, 0); + startIndex += 64 - _dataSize; + totalArrayLength -= 64; + while (totalArrayLength >= 64) + { + Array.Copy(array, startIndex, _data, 0, 64); + MD5Core.GetHashBlock(array, ref _abcd, startIndex); + totalArrayLength -= 64; + startIndex += 64; + } + _dataSize = totalArrayLength; + Array.Copy(array, startIndex, _data, 0, totalArrayLength); + } + else + { + Array.Copy(array, startIndex, _data, _dataSize, cbSize); + _dataSize = totalArrayLength; + } + _totalLength += cbSize; + } + + protected override byte[] HashFinal() + { + HashValue = MD5Core.GetHashFinalBlock(_data, 0, _dataSize, _abcd, _totalLength * 8); + return HashValue; + } + + byte[] _data; + MD5Core.ABCDStruct _abcd; + Int64 _totalLength; + int _dataSize; + + static class MD5Core + { +#if true + public static byte[] GetHash(byte[] input) + { + if (null == input) + throw new ArgumentNullException("input"); + + // Intitial values defined in RFC 1321. + ABCDStruct abcd = new ABCDStruct(); + abcd.A = A; + abcd.B = B; + abcd.C = C; + abcd.D = D; + + // We pass in the input array by block, the final block of data must be handled specially for padding & length embeding. + int startIndex = 0; + while (startIndex <= input.Length - 64) + { + GetHashBlock(input, ref abcd, startIndex); + startIndex += 64; + } + // The final data block. + return GetHashFinalBlock(input, startIndex, input.Length - startIndex, abcd, (Int64)input.Length * 8); + } +#endif + + internal static byte[] GetHashFinalBlock(byte[] input, int ibStart, int cbSize, ABCDStruct abcd, Int64 len) + { + byte[] working = new byte[64]; + byte[] length = BitConverter.GetBytes(len); + + // Padding is a single bit 1, followed by the number of 0s required to make size congruent to 448 modulo 512. Step 1 of RFC 1321 + // The CLR ensures that our buffer is 0-assigned, we don't need to explicitly set it. This is why it ends up being quicker to just + // use a temporary array rather then doing in-place assignment (5% for small inputs) + Array.Copy(input, ibStart, working, 0, cbSize); + working[cbSize] = 0x80; + + // We have enough room to store the length in this chunk. + if (cbSize <= 56) + { + Array.Copy(length, 0, working, 56, 8); + GetHashBlock(working, ref abcd, 0); + } + else // We need an aditional chunk to store the length. + { + GetHashBlock(working, ref abcd, 0); + // Create an entirely new chunk due to the 0-assigned trick mentioned above, to avoid an extra function call clearing the array. + working = new byte[64]; + Array.Copy(length, 0, working, 56, 8); + GetHashBlock(working, ref abcd, 0); + } + byte[] output = new byte[16]; + Array.Copy(BitConverter.GetBytes(abcd.A), 0, output, 0, 4); + Array.Copy(BitConverter.GetBytes(abcd.B), 0, output, 4, 4); + Array.Copy(BitConverter.GetBytes(abcd.C), 0, output, 8, 4); + Array.Copy(BitConverter.GetBytes(abcd.D), 0, output, 12, 4); + return output; + } + + internal static void GetHashBlock(byte[] input, ref ABCDStruct ABCDValue, int ibStart) + { + uint[] temp = Converter(input, ibStart); + uint a = ABCDValue.A; + uint b = ABCDValue.B; + uint c = ABCDValue.C; + uint d = ABCDValue.D; + + a = r1(a, b, c, d, temp[0], 7, 0xd76aa478); + d = r1(d, a, b, c, temp[1], 12, 0xe8c7b756); + c = r1(c, d, a, b, temp[2], 17, 0x242070db); + b = r1(b, c, d, a, temp[3], 22, 0xc1bdceee); + a = r1(a, b, c, d, temp[4], 7, 0xf57c0faf); + d = r1(d, a, b, c, temp[5], 12, 0x4787c62a); + c = r1(c, d, a, b, temp[6], 17, 0xa8304613); + b = r1(b, c, d, a, temp[7], 22, 0xfd469501); + a = r1(a, b, c, d, temp[8], 7, 0x698098d8); + d = r1(d, a, b, c, temp[9], 12, 0x8b44f7af); + c = r1(c, d, a, b, temp[10], 17, 0xffff5bb1); + b = r1(b, c, d, a, temp[11], 22, 0x895cd7be); + a = r1(a, b, c, d, temp[12], 7, 0x6b901122); + d = r1(d, a, b, c, temp[13], 12, 0xfd987193); + c = r1(c, d, a, b, temp[14], 17, 0xa679438e); + b = r1(b, c, d, a, temp[15], 22, 0x49b40821); + + a = r2(a, b, c, d, temp[1], 5, 0xf61e2562); + d = r2(d, a, b, c, temp[6], 9, 0xc040b340); + c = r2(c, d, a, b, temp[11], 14, 0x265e5a51); + b = r2(b, c, d, a, temp[0], 20, 0xe9b6c7aa); + a = r2(a, b, c, d, temp[5], 5, 0xd62f105d); + d = r2(d, a, b, c, temp[10], 9, 0x02441453); + c = r2(c, d, a, b, temp[15], 14, 0xd8a1e681); + b = r2(b, c, d, a, temp[4], 20, 0xe7d3fbc8); + a = r2(a, b, c, d, temp[9], 5, 0x21e1cde6); + d = r2(d, a, b, c, temp[14], 9, 0xc33707d6); + c = r2(c, d, a, b, temp[3], 14, 0xf4d50d87); + b = r2(b, c, d, a, temp[8], 20, 0x455a14ed); + a = r2(a, b, c, d, temp[13], 5, 0xa9e3e905); + d = r2(d, a, b, c, temp[2], 9, 0xfcefa3f8); + c = r2(c, d, a, b, temp[7], 14, 0x676f02d9); + b = r2(b, c, d, a, temp[12], 20, 0x8d2a4c8a); + + a = r3(a, b, c, d, temp[5], 4, 0xfffa3942); + d = r3(d, a, b, c, temp[8], 11, 0x8771f681); + c = r3(c, d, a, b, temp[11], 16, 0x6d9d6122); + b = r3(b, c, d, a, temp[14], 23, 0xfde5380c); + a = r3(a, b, c, d, temp[1], 4, 0xa4beea44); + d = r3(d, a, b, c, temp[4], 11, 0x4bdecfa9); + c = r3(c, d, a, b, temp[7], 16, 0xf6bb4b60); + b = r3(b, c, d, a, temp[10], 23, 0xbebfbc70); + a = r3(a, b, c, d, temp[13], 4, 0x289b7ec6); + d = r3(d, a, b, c, temp[0], 11, 0xeaa127fa); + c = r3(c, d, a, b, temp[3], 16, 0xd4ef3085); + b = r3(b, c, d, a, temp[6], 23, 0x04881d05); + a = r3(a, b, c, d, temp[9], 4, 0xd9d4d039); + d = r3(d, a, b, c, temp[12], 11, 0xe6db99e5); + c = r3(c, d, a, b, temp[15], 16, 0x1fa27cf8); + b = r3(b, c, d, a, temp[2], 23, 0xc4ac5665); + + a = r4(a, b, c, d, temp[0], 6, 0xf4292244); + d = r4(d, a, b, c, temp[7], 10, 0x432aff97); + c = r4(c, d, a, b, temp[14], 15, 0xab9423a7); + b = r4(b, c, d, a, temp[5], 21, 0xfc93a039); + a = r4(a, b, c, d, temp[12], 6, 0x655b59c3); + d = r4(d, a, b, c, temp[3], 10, 0x8f0ccc92); + c = r4(c, d, a, b, temp[10], 15, 0xffeff47d); + b = r4(b, c, d, a, temp[1], 21, 0x85845dd1); + a = r4(a, b, c, d, temp[8], 6, 0x6fa87e4f); + d = r4(d, a, b, c, temp[15], 10, 0xfe2ce6e0); + c = r4(c, d, a, b, temp[6], 15, 0xa3014314); + b = r4(b, c, d, a, temp[13], 21, 0x4e0811a1); + a = r4(a, b, c, d, temp[4], 6, 0xf7537e82); + d = r4(d, a, b, c, temp[11], 10, 0xbd3af235); + c = r4(c, d, a, b, temp[2], 15, 0x2ad7d2bb); + b = r4(b, c, d, a, temp[9], 21, 0xeb86d391); + + ABCDValue.A = unchecked(a + ABCDValue.A); + ABCDValue.B = unchecked(b + ABCDValue.B); + ABCDValue.C = unchecked(c + ABCDValue.C); + ABCDValue.D = unchecked(d + ABCDValue.D); + } + + // Manually unrolling these equations nets us a 20% performance improvement + private static uint r1(uint a, uint b, uint c, uint d, uint x, int s, uint t) + { + // (b + LSR((a + F(b, c, d) + x + t), s)) + // F(x, y, z) ((x & y) | ((x ^ 0xFFFFFFFF) & z)) + return unchecked(b + LSR((a + ((b & c) | ((b ^ 0xFFFFFFFF) & d)) + x + t), s)); + } + + private static uint r2(uint a, uint b, uint c, uint d, uint x, int s, uint t) + { + // (b + LSR((a + G(b, c, d) + x + t), s)) + // G(x, y, z) ((x & z) | (y & (z ^ 0xFFFFFFFF))) + return unchecked(b + LSR((a + ((b & d) | (c & (d ^ 0xFFFFFFFF))) + x + t), s)); + } + + private static uint r3(uint a, uint b, uint c, uint d, uint x, int s, uint t) + { + // (b + LSR((a + H(b, c, d) + k + i), s)) + // H(x, y, z) (x ^ y ^ z) + return unchecked(b + LSR((a + (b ^ c ^ d) + x + t), s)); + } + + private static uint r4(uint a, uint b, uint c, uint d, uint x, int s, uint t) + { + // (b + LSR((a + I(b, c, d) + k + i), s)) + // I(x, y, z) (y ^ (x | (z ^ 0xFFFFFFFF))) + return unchecked(b + LSR((a + (c ^ (b | (d ^ 0xFFFFFFFF))) + x + t), s)); + } + + // Implementation of left rotate + // s is an int instead of a uint becuase the CLR requires the argument passed to >>/<< is of + // type int. Doing the demoting inside this function would add overhead. + private static uint LSR(uint i, int s) + { + return (i << s) | (i >> (32 - s)); + } + + // Convert input array into array of UInts. + static uint[] Converter(byte[] input, int ibStart) + { + if (null == input) + throw new ArgumentNullException("input"); + + uint[] result = new uint[16]; + for (int idx = 0; idx < 16; idx++) + { + result[idx] = (uint)input[ibStart + idx * 4]; + result[idx] += (uint)input[ibStart + idx * 4 + 1] << 8; + result[idx] += (uint)input[ibStart + idx * 4 + 2] << 16; + result[idx] += (uint)input[ibStart + idx * 4 + 3] << 24; + + Debug.Assert(result[idx] == + (input[ibStart + idx * 4]) + + ((uint)input[ibStart + idx * 4 + 1] << 8) + + ((uint)input[ibStart + idx * 4 + 2] << 16) + + ((uint)input[ibStart + idx * 4 + 3] << 24)); + } + return result; + } + + // Simple struct for the (a,b,c,d) which is used to compute the mesage digest. + public struct ABCDStruct + { + public uint A; + public uint B; + public uint C; + public uint D; + } + } + } + +#if GDI && DEBUG && true_ + + // See here for details: http://archive.msdn.microsoft.com/SilverlightMD5/WorkItem/View.aspx?WorkItemId=3 + + public static class TestMD5 + { + public static void Test() + { + Random rnd = new Random(); + for (int i = 0; i < 10000; i++) + { + int count = rnd.Next(1000) + 1; + Console.WriteLine(String.Format("{0}: {1}", i, count)); + Test2(count); + } + } + + static void Test2(int count) + { + byte[] bytes = new byte[count]; + + for (int idx = 0; idx < count; idx += 16) + Array.Copy(Guid.NewGuid().ToByteArray(), 0, bytes, idx, Math.Min(16, count - idx)); + + MD5 md5dotNet = new MD5CryptoServiceProvider(); + md5dotNet.Initialize(); + MD5Managed md5m = new MD5Managed(); + md5m.Initialize(); + + byte[] result1 = md5dotNet.ComputeHash(bytes); + byte[] result2 = md5m.ComputeHash(bytes); + + if (!CompareBytes(result1, result2)) + { + count.GetType(); + //throw new Exception("Bug in MD5Managed..."); + } + } + + static bool CompareBytes(byte[] bytes1, byte[] bytes2) + { + for (int idx = 0; idx < bytes1.Length; idx++) + { + if (bytes1[idx] != bytes2[idx]) + return false; + } + return true; + } + } +#endif +} +#endif \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecurityHandler.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecurityHandler.cs new file mode 100644 index 00000000..fea9cbf0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecurityHandler.cs @@ -0,0 +1,137 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Pdf.Security +{ + /// + /// Represents the base of all security handlers. + /// + public abstract class PdfSecurityHandler : PdfDictionary + { + internal PdfSecurityHandler(PdfDocument document) + : base(document) + { } + + internal PdfSecurityHandler(PdfDictionary dict) + : base(dict) + { } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + /// + /// (Required) The name of the preferred security handler for this document. Typically, + /// it is the name of the security handler that was used to encrypt the document. If + /// SubFilter is not present, only this security handler should be used when opening + /// the document. If it is present, consumer applications can use any security handler + /// that implements the format specified by SubFilter. + /// Standard is the name of the built-in password-based security handler. Names for other + /// security handlers can be registered by using the procedure described in Appendix E. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string Filter = "/Filter"; + + /// + /// (Optional; PDF 1.3) A name that completely specifies the format and interpretation of + /// the contents of the encryption dictionary. It is needed to allow security handlers other + /// than the one specified by Filter to decrypt the document. If this entry is absent, other + /// security handlers should not be allowed to decrypt the document. + /// + [KeyInfo("1.3", KeyType.Name | KeyType.Optional)] + public const string SubFilter = "/SubFilter"; + + /// + /// (Optional but strongly recommended) A code specifying the algorithm to be used in encrypting + /// and decrypting the document: + /// 0 An algorithm that is undocumented and no longer supported, and whose use is strongly discouraged. + /// 1 Algorithm 3.1, with an encryption key length of 40 bits. + /// 2 (PDF 1.4) Algorithm 3.1, but permitting encryption key lengths greater than 40 bits. + /// 3 (PDF 1.4) An unpublished algorithm that permits encryption key lengths ranging from 40 to 128 bits. + /// 4 (PDF 1.5) The security handler defines the use of encryption and decryption in the document, using + /// the rules specified by the CF, StmF, and StrF entries. + /// The default value if this entry is omitted is 0, but a value of 1 or greater is strongly recommended. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string V = "/V"; + + /// + /// (Optional; PDF 1.4; only if V is 2 or 3) The length of the encryption key, in bits. + /// The value must be a multiple of 8, in the range 40 to 128. Default value: 40. + /// + [KeyInfo("1.4", KeyType.Integer | KeyType.Optional)] + public const string Length = "/Length"; + + /// + /// (Optional; meaningful only when the value of V is 4; PDF 1.5) + /// A dictionary whose keys are crypt filter names and whose values are the corresponding + /// crypt filter dictionaries. Every crypt filter used in the document must have an entry + /// in this dictionary, except for the standard crypt filter names. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string CF = "/CF"; + + /// + /// (Optional; meaningful only when the value of V is 4; PDF 1.5) + /// The name of the crypt filter that is used by default when decrypting streams. + /// The name must be a key in the CF dictionary or a standard crypt filter name. All streams + /// in the document, except for cross-reference streams or streams that have a Crypt entry in + /// their Filter array, are decrypted by the security handler, using this crypt filter. + /// Default value: Identity. + /// + [KeyInfo("1.5", KeyType.Name | KeyType.Optional)] + public const string StmF = "/StmF"; + + /// + /// (Optional; meaningful only when the value of V is 4; PDF 1.) + /// The name of the crypt filter that is used when decrypting all strings in the document. + /// The name must be a key in the CF dictionary or a standard crypt filter name. + /// Default value: Identity. + /// + [KeyInfo("1.5", KeyType.Name | KeyType.Optional)] + public const string StrF = "/StrF"; + + /// + /// (Optional; meaningful only when the value of V is 4; PDF 1.6) + /// The name of the crypt filter that should be used by default when encrypting embedded + /// file streams; it must correspond to a key in the CF dictionary or a standard crypt + /// filter name. This entry is provided by the security handler. Applications should respect + /// this value when encrypting embedded files, except for embedded file streams that have + /// their own crypt filter specifier. If this entry is not present, and the embedded file + /// stream does not contain a crypt filter specifier, the stream should be encrypted using + /// the default stream crypt filter specified by StmF. + /// + [KeyInfo("1.6", KeyType.Name | KeyType.Optional)] + public const string EFF = "/EFF"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecuritySettings.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecuritySettings.cs new file mode 100644 index 00000000..bb1bb062 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfSecuritySettings.cs @@ -0,0 +1,253 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Security +{ + /// + /// Encapsulates access to the security settings of a PDF document. + /// + public sealed class PdfSecuritySettings + { + internal PdfSecuritySettings(PdfDocument document) + { + _document = document; + } + readonly PdfDocument _document; + + /// + /// Indicates whether the granted access to the document is 'owner permission'. Returns true if the document + /// is unprotected or was opened with the owner password. Returns false if the document was opened with the + /// user password. + /// + public bool HasOwnerPermissions + { + get { return _hasOwnerPermissions; } + } + internal bool _hasOwnerPermissions = true; + + /// + /// Gets or sets the document security level. If you set the security level to anything but PdfDocumentSecurityLevel.None + /// you must also set a user and/or an owner password. Otherwise saving the document will fail. + /// + public PdfDocumentSecurityLevel DocumentSecurityLevel + { + get { return _documentSecurityLevel; } + set { _documentSecurityLevel = value; } + } + PdfDocumentSecurityLevel _documentSecurityLevel; + + /// + /// Sets the user password of the document. Setting a password automatically sets the + /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current + /// value is PdfDocumentSecurityLevel.None. + /// + public string UserPassword + { + set { SecurityHandler.UserPassword = value; } + } + + /// + /// Sets the owner password of the document. Setting a password automatically sets the + /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current + /// value is PdfDocumentSecurityLevel.None. + /// + public string OwnerPassword + { + set { SecurityHandler.OwnerPassword = value; } + } + + /// + /// Determines whether the document can be saved. + /// + internal bool CanSave(ref string message) + { + if (_documentSecurityLevel != PdfDocumentSecurityLevel.None) + { + if (String.IsNullOrEmpty(SecurityHandler._userPassword) && String.IsNullOrEmpty(SecurityHandler._ownerPassword)) + { + message = PSSR.UserOrOwnerPasswordRequired; + return false; + } + } + return true; + } + + #region Permissions + //TODO: Use documentation from our English Acrobat 6.0 version. + + /// + /// Permits printing the document. Should be used in conjunction with PermitFullQualityPrint. + /// + public bool PermitPrint + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitPrint) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitPrint; + else + permission &= ~PdfUserAccessPermission.PermitPrint; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits modifying the document. + /// + public bool PermitModifyDocument + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitModifyDocument) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitModifyDocument; + else + permission &= ~PdfUserAccessPermission.PermitModifyDocument; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits content copying or extraction. + /// + public bool PermitExtractContent + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitExtractContent) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitExtractContent; + else + permission &= ~PdfUserAccessPermission.PermitExtractContent; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits commenting the document. + /// + public bool PermitAnnotations + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitAnnotations) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitAnnotations; + else + permission &= ~PdfUserAccessPermission.PermitAnnotations; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits filling of form fields. + /// + public bool PermitFormsFill + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitFormsFill) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitFormsFill; + else + permission &= ~PdfUserAccessPermission.PermitFormsFill; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits content extraction for accessibility. + /// + public bool PermitAccessibilityExtractContent + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitAccessibilityExtractContent) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitAccessibilityExtractContent; + else + permission &= ~PdfUserAccessPermission.PermitAccessibilityExtractContent; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits to insert, rotate, or delete pages and create bookmarks or thumbnail images even if + /// PermitModifyDocument is not set. + /// + public bool PermitAssembleDocument + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitAssembleDocument) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitAssembleDocument; + else + permission &= ~PdfUserAccessPermission.PermitAssembleDocument; + SecurityHandler.Permission = permission; + } + } + + /// + /// Permits to print in high quality. insert, rotate, or delete pages and create bookmarks or thumbnail images + /// even if PermitModifyDocument is not set. + /// + public bool PermitFullQualityPrint + { + get { return (SecurityHandler.Permission & PdfUserAccessPermission.PermitFullQualityPrint) != 0; } + set + { + PdfUserAccessPermission permission = SecurityHandler.Permission; + if (value) + permission |= PdfUserAccessPermission.PermitFullQualityPrint; + else + permission &= ~PdfUserAccessPermission.PermitFullQualityPrint; + SecurityHandler.Permission = permission; + } + } + #endregion + + /// + /// PdfStandardSecurityHandler is the only implemented handler. + /// + internal PdfStandardSecurityHandler SecurityHandler + { + get { return _document._trailer.SecurityHandler; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs new file mode 100644 index 00000000..4bebef22 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/PdfStandardSecurityHandler.cs @@ -0,0 +1,737 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Pdf; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Internal; +#if !NETFX_CORE && !UWP +using System.Security.Cryptography; +#endif + +#pragma warning disable 0169 +#pragma warning disable 0649 + +namespace PdfSharp.Pdf.Security +{ + /// + /// Represents the standard PDF security handler. + /// + public sealed class PdfStandardSecurityHandler : PdfSecurityHandler + { + internal PdfStandardSecurityHandler(PdfDocument document) + : base(document) + { } + + internal PdfStandardSecurityHandler(PdfDictionary dict) + : base(dict) + { } + + /// + /// Sets the user password of the document. Setting a password automatically sets the + /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current + /// value is PdfDocumentSecurityLevel.None. + /// + public string UserPassword + { + set + { + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None) + _document._securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit; + _userPassword = value; + } + } + internal string _userPassword; + + /// + /// Sets the owner password of the document. Setting a password automatically sets the + /// PdfDocumentSecurityLevel to PdfDocumentSecurityLevel.Encrypted128Bit if its current + /// value is PdfDocumentSecurityLevel.None. + /// + public string OwnerPassword + { + set + { + if (_document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.None) + _document._securitySettings.DocumentSecurityLevel = PdfDocumentSecurityLevel.Encrypted128Bit; + _ownerPassword = value; + } + } + internal string _ownerPassword; + + /// + /// Gets or sets the user access permission represented as an integer in the P key. + /// + internal PdfUserAccessPermission Permission + { + get + { + PdfUserAccessPermission permission = (PdfUserAccessPermission)Elements.GetInteger(Keys.P); + if ((int)permission == 0) + permission = PdfUserAccessPermission.PermitAll; + return permission; + } + set { Elements.SetInteger(Keys.P, (int)value); } + } + + /// + /// Encrypts the whole document. + /// + public void EncryptDocument() + { + foreach (PdfReference iref in _document._irefTable.AllReferences) + { + if (!ReferenceEquals(iref.Value, this)) + EncryptObject(iref.Value); + } + } + + /// + /// Encrypts an indirect object. + /// + internal void EncryptObject(PdfObject value) + { + Debug.Assert(value.Reference != null); + + SetHashKey(value.ObjectID); +#if DEBUG + if (value.ObjectID.ObjectNumber == 10) + GetType(); +#endif + + PdfDictionary dict; + PdfArray array; + PdfStringObject str; + if ((dict = value as PdfDictionary) != null) + EncryptDictionary(dict); + else if ((array = value as PdfArray) != null) + EncryptArray(array); + else if ((str = value as PdfStringObject) != null) + { + if (str.Length != 0) + { + byte[] bytes = str.EncryptionValue; + PrepareKey(); + EncryptRC4(bytes); + str.EncryptionValue = bytes; + } + } + } + + /// + /// Encrypts a dictionary. + /// + void EncryptDictionary(PdfDictionary dict) + { + PdfName[] names = dict.Elements.KeyNames; + foreach (KeyValuePair item in dict.Elements) + { + PdfString value1; + PdfDictionary value2; + PdfArray value3; + if ((value1 = item.Value as PdfString) != null) + EncryptString(value1); + else if ((value2 = item.Value as PdfDictionary) != null) + EncryptDictionary(value2); + else if ((value3 = item.Value as PdfArray) != null) + EncryptArray(value3); + } + if (dict.Stream != null) + { + byte[] bytes = dict.Stream.Value; + if (bytes.Length != 0) + { + PrepareKey(); + EncryptRC4(bytes); + dict.Stream.Value = bytes; + } + } + } + + /// + /// Encrypts an array. + /// + void EncryptArray(PdfArray array) + { + int count = array.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = array.Elements[idx]; + PdfString value1; + PdfDictionary value2; + PdfArray value3; + if ((value1 = item as PdfString) != null) + EncryptString(value1); + else if ((value2 = item as PdfDictionary) != null) + EncryptDictionary(value2); + else if ((value3 = item as PdfArray) != null) + EncryptArray(value3); + } + } + + /// + /// Encrypts a string. + /// + void EncryptString(PdfString value) + { + if (value.Length != 0) + { + byte[] bytes = value.EncryptionValue; + PrepareKey(); + EncryptRC4(bytes); + value.EncryptionValue = bytes; + } + } + + /// + /// Encrypts an array. + /// + internal byte[] EncryptBytes(byte[] bytes) + { + if (bytes != null && bytes.Length != 0) + { + PrepareKey(); + EncryptRC4(bytes); + } + return bytes; + } + + #region Encryption Algorithms + + /// + /// Checks the password. + /// + /// Password or null if no password is provided. + public PasswordValidity ValidatePassword(string inputPassword) + { + // We can handle 40 and 128 bit standard encryption. + string filter = Elements.GetName(PdfSecurityHandler.Keys.Filter); + int v = Elements.GetInteger(PdfSecurityHandler.Keys.V); + if (filter != "/Standard" || !(v >= 1 && v <= 3)) + throw new PdfReaderException(PSSR.UnknownEncryption); + + byte[] documentID = PdfEncoders.RawEncoding.GetBytes(Owner.Internals.FirstDocumentID); + byte[] oValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.O)); + byte[] uValue = PdfEncoders.RawEncoding.GetBytes(Elements.GetString(Keys.U)); + int pValue = Elements.GetInteger(Keys.P); + int rValue = Elements.GetInteger(Keys.R); + + if (inputPassword == null) + inputPassword = ""; + + bool strongEncryption = rValue == 3; + int keyLength = strongEncryption ? 16 : 32; + + // Try owner password first. + //byte[] password = PdfEncoders.RawEncoding.GetBytes(inputPassword); + InitWithOwnerPassword(documentID, inputPassword, oValue, pValue, strongEncryption); + if (EqualsKey(uValue, keyLength)) + { + _document.SecuritySettings._hasOwnerPermissions = true; + return PasswordValidity.OwnerPassword; + } + _document.SecuritySettings._hasOwnerPermissions = false; + + // Now try user password. + //password = PdfEncoders.RawEncoding.GetBytes(inputPassword); + InitWithUserPassword(documentID, inputPassword, oValue, pValue, strongEncryption); + if (EqualsKey(uValue, keyLength)) + return PasswordValidity.UserPassword; + return PasswordValidity.Invalid; + } + + [Conditional("DEBUG")] + static void DumpBytes(string tag, byte[] bytes) + { + string dump = tag + ": "; + for (int idx = 0; idx < bytes.Length; idx++) + dump += String.Format("{0:X2}", bytes[idx]); + Debug.WriteLine(dump); + } + + /// + /// Pads a password to a 32 byte array. + /// + static byte[] PadPassword(string password) + { + byte[] padded = new byte[32]; + if (password == null) + Array.Copy(PasswordPadding, 0, padded, 0, 32); + else + { + int length = password.Length; + Array.Copy(PdfEncoders.RawEncoding.GetBytes(password), 0, padded, 0, Math.Min(length, 32)); + if (length < 32) + Array.Copy(PasswordPadding, 0, padded, length, 32 - length); + } + return padded; + } + static readonly byte[] PasswordPadding = // 32 bytes password padding defined by Adobe + { + 0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, + 0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A, + }; + + /// + /// Generates the user key based on the padded user password. + /// + void InitWithUserPassword(byte[] documentID, string userPassword, byte[] ownerKey, int permissions, bool strongEncryption) + { + InitEncryptionKey(documentID, PadPassword(userPassword), ownerKey, permissions, strongEncryption); + SetupUserKey(documentID); + } + + /// + /// Generates the user key based on the padded owner password. + /// + void InitWithOwnerPassword(byte[] documentID, string ownerPassword, byte[] ownerKey, int permissions, bool strongEncryption) + { + byte[] userPad = ComputeOwnerKey(ownerKey, PadPassword(ownerPassword), strongEncryption); + InitEncryptionKey(documentID, userPad, ownerKey, permissions, strongEncryption); + SetupUserKey(documentID); + } + + /// + /// Computes the padded user password from the padded owner password. + /// + byte[] ComputeOwnerKey(byte[] userPad, byte[] ownerPad, bool strongEncryption) + { + byte[] ownerKey = new byte[32]; + //#if !SILVERLIGHT + byte[] digest = _md5.ComputeHash(ownerPad); + if (strongEncryption) + { + byte[] mkey = new byte[16]; + // Hash the pad 50 times + for (int idx = 0; idx < 50; idx++) + digest = _md5.ComputeHash(digest); + Array.Copy(userPad, 0, ownerKey, 0, 32); + // Encrypt the key + for (int i = 0; i < 20; i++) + { + for (int j = 0; j < mkey.Length; ++j) + mkey[j] = (byte)(digest[j] ^ i); + PrepareRC4Key(mkey); + EncryptRC4(ownerKey); + } + } + else + { + PrepareRC4Key(digest, 0, 5); + EncryptRC4(userPad, ownerKey); + } + //#endif + return ownerKey; + } + + /// + /// Computes the encryption key. + /// + void InitEncryptionKey(byte[] documentID, byte[] userPad, byte[] ownerKey, int permissions, bool strongEncryption) + { + //#if !SILVERLIGHT + _ownerKey = ownerKey; + _encryptionKey = new byte[strongEncryption ? 16 : 5]; + +#if !NETFX_CORE && !DNC10 + _md5.Initialize(); + _md5.TransformBlock(userPad, 0, userPad.Length, userPad, 0); + _md5.TransformBlock(ownerKey, 0, ownerKey.Length, ownerKey, 0); + + // Split permission into 4 bytes + byte[] permission = new byte[4]; + permission[0] = (byte)permissions; + permission[1] = (byte)(permissions >> 8); + permission[2] = (byte)(permissions >> 16); + permission[3] = (byte)(permissions >> 24); + _md5.TransformBlock(permission, 0, 4, permission, 0); + _md5.TransformBlock(documentID, 0, documentID.Length, documentID, 0); + _md5.TransformFinalBlock(permission, 0, 0); + byte[] digest = _md5.Hash; + _md5.Initialize(); + // Create the hash 50 times (only for 128 bit) + if (_encryptionKey.Length == 16) + { + for (int idx = 0; idx < 50; idx++) + { + digest = _md5.ComputeHash(digest); + _md5.Initialize(); + } + } + Array.Copy(digest, 0, _encryptionKey, 0, _encryptionKey.Length); + //#endif +#endif + } + + /// + /// Computes the user key. + /// + void SetupUserKey(byte[] documentID) + { +#if !NETFX_CORE && !DNC10 + //#if !SILVERLIGHT + if (_encryptionKey.Length == 16) + { + _md5.TransformBlock(PasswordPadding, 0, PasswordPadding.Length, PasswordPadding, 0); + _md5.TransformFinalBlock(documentID, 0, documentID.Length); + byte[] digest = _md5.Hash; + _md5.Initialize(); + Array.Copy(digest, 0, _userKey, 0, 16); + for (int idx = 16; idx < 32; idx++) + _userKey[idx] = 0; + //Encrypt the key + for (int i = 0; i < 20; i++) + { + for (int j = 0; j < _encryptionKey.Length; j++) + digest[j] = (byte)(_encryptionKey[j] ^ i); + PrepareRC4Key(digest, 0, _encryptionKey.Length); + EncryptRC4(_userKey, 0, 16); + } + } + else + { + PrepareRC4Key(_encryptionKey); + EncryptRC4(PasswordPadding, _userKey); + } + //#endif +#endif + } + + /// + /// Prepare the encryption key. + /// + void PrepareKey() + { + if (_key != null && _keySize > 0) //!!!mod 2017-11-06 Added "if" because PrepareRC4Key fails if _key is null. But _key appears to be always null, so maybe PrepareKey() is obsolete. + PrepareRC4Key(_key, 0, _keySize); + } + + /// + /// Prepare the encryption key. + /// + void PrepareRC4Key(byte[] key) + { + PrepareRC4Key(key, 0, key.Length); + } + + /// + /// Prepare the encryption key. + /// + void PrepareRC4Key(byte[] key, int offset, int length) + { + int idx1 = 0; + int idx2 = 0; + for (int idx = 0; idx < 256; idx++) + _state[idx] = (byte)idx; + byte tmp; + for (int idx = 0; idx < 256; idx++) + { + idx2 = (key[idx1 + offset] + _state[idx] + idx2) & 255; + tmp = _state[idx]; + _state[idx] = _state[idx2]; + _state[idx2] = tmp; + idx1 = (idx1 + 1) % length; + } + } + + /// + /// Encrypts the data. + /// + // ReSharper disable InconsistentNaming + void EncryptRC4(byte[] data) + // ReSharper restore InconsistentNaming + { + EncryptRC4(data, 0, data.Length, data); + } + + /// + /// Encrypts the data. + /// + // ReSharper disable InconsistentNaming + void EncryptRC4(byte[] data, int offset, int length) + // ReSharper restore InconsistentNaming + { + EncryptRC4(data, offset, length, data); + } + + /// + /// Encrypts the data. + /// + void EncryptRC4(byte[] inputData, byte[] outputData) + { + EncryptRC4(inputData, 0, inputData.Length, outputData); + } + + /// + /// Encrypts the data. + /// + void EncryptRC4(byte[] inputData, int offset, int length, byte[] outputData) + { + length += offset; + int x = 0, y = 0; + byte b; + for (int idx = offset; idx < length; idx++) + { + x = (x + 1) & 255; + y = (_state[x] + y) & 255; + b = _state[x]; + _state[x] = _state[y]; + _state[y] = b; + outputData[idx] = (byte)(inputData[idx] ^ _state[(_state[x] + _state[y]) & 255]); + } + } + + /// + /// Checks whether the calculated key correct. + /// + bool EqualsKey(byte[] value, int length) + { + for (int idx = 0; idx < length; idx++) + { + if (_userKey[idx] != value[idx]) + return false; + } + return true; + } + + /// + /// Set the hash key for the specified object. + /// + internal void SetHashKey(PdfObjectID id) + { +#if !NETFX_CORE && !DNC10 + //#if !SILVERLIGHT + byte[] objectId = new byte[5]; + _md5.Initialize(); + // Split the object number and generation + objectId[0] = (byte)id.ObjectNumber; + objectId[1] = (byte)(id.ObjectNumber >> 8); + objectId[2] = (byte)(id.ObjectNumber >> 16); + objectId[3] = (byte)id.GenerationNumber; + objectId[4] = (byte)(id.GenerationNumber >> 8); + _md5.TransformBlock(_encryptionKey, 0, _encryptionKey.Length, _encryptionKey, 0); + _md5.TransformFinalBlock(objectId, 0, objectId.Length); + _key = _md5.Hash; + _md5.Initialize(); + _keySize = _encryptionKey.Length + 5; + if (_keySize > 16) + _keySize = 16; + //#endif +#endif + } + + /// + /// Prepares the security handler for encrypting the document. + /// + public void PrepareEncryption() + { + //#if !SILVERLIGHT + Debug.Assert(_document._securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None); + int permissions = (int)Permission; + bool strongEncryption = _document._securitySettings.DocumentSecurityLevel == PdfDocumentSecurityLevel.Encrypted128Bit; + + PdfInteger vValue; + PdfInteger length; + PdfInteger rValue; + + if (strongEncryption) + { + vValue = new PdfInteger(2); + length = new PdfInteger(128); + rValue = new PdfInteger(3); + } + else + { + vValue = new PdfInteger(1); + length = new PdfInteger(40); + rValue = new PdfInteger(2); + } + + if (String.IsNullOrEmpty(_userPassword)) + _userPassword = ""; + // Use user password twice if no owner password provided. + if (String.IsNullOrEmpty(_ownerPassword)) + _ownerPassword = _userPassword; + + // Correct permission bits + permissions |= (int)(strongEncryption ? (uint)0xfffff0c0 : (uint)0xffffffc0); + permissions &= unchecked((int)0xfffffffc); + + PdfInteger pValue = new PdfInteger(permissions); + + Debug.Assert(_ownerPassword.Length > 0, "Empty owner password."); + byte[] userPad = PadPassword(_userPassword); + byte[] ownerPad = PadPassword(_ownerPassword); + + _md5.Initialize(); + _ownerKey = ComputeOwnerKey(userPad, ownerPad, strongEncryption); + byte[] documentID = PdfEncoders.RawEncoding.GetBytes(_document.Internals.FirstDocumentID); + InitWithUserPassword(documentID, _userPassword, _ownerKey, permissions, strongEncryption); + + PdfString oValue = new PdfString(PdfEncoders.RawEncoding.GetString(_ownerKey, 0, _ownerKey.Length)); + PdfString uValue = new PdfString(PdfEncoders.RawEncoding.GetString(_userKey, 0, _userKey.Length)); + + Elements[Keys.Filter] = new PdfName("/Standard"); + Elements[Keys.V] = vValue; + Elements[Keys.Length] = length; + Elements[Keys.R] = rValue; + Elements[Keys.O] = oValue; + Elements[Keys.U] = uValue; + Elements[Keys.P] = pValue; + //#endif + } + + /// + /// The global encryption key. + /// + byte[] _encryptionKey; + +#if !SILVERLIGHT && !UWP + /// + /// The message digest algorithm MD5. + /// + readonly MD5 _md5 = new MD5CryptoServiceProvider(); +#if DEBUG_ + readonly MD5Managed _md5M = new MD5Managed(); +#endif +#else + readonly MD5Managed _md5 = new MD5Managed(); +#endif +#if NETFX_CORE + // readonly MD5Managed _md5 = new MD5Managed(); +#endif + /// + /// Bytes used for RC4 encryption. + /// + readonly byte[] _state = new byte[256]; + + /// + /// The encryption key for the owner. + /// + byte[] _ownerKey = new byte[32]; + + /// + /// The encryption key for the user. + /// + readonly byte[] _userKey = new byte[32]; + + /// + /// The encryption key for a particular object/generation. + /// + byte[] _key; + + /// + /// The encryption key length for a particular object/generation. + /// + int _keySize; + + #endregion + + internal override void WriteObject(PdfWriter writer) + { + // Don't encrypt myself. + PdfStandardSecurityHandler securityHandler = writer.SecurityHandler; + writer.SecurityHandler = null; + base.WriteObject(writer); + writer.SecurityHandler = securityHandler; + } + + #region Keys + /// + /// Predefined keys of this dictionary. + /// + internal sealed new class Keys : PdfSecurityHandler.Keys + { + /// + /// (Required) A number specifying which revision of the standard security handler + /// should be used to interpret this dictionary: + /// 2 if the document is encrypted with a V value less than 2 and does not have any of + /// the access permissions set (by means of the P entry, below) that are designated + /// "Revision 3 or greater". + /// 3 if the document is encrypted with a V value of 2 or 3, or has any "Revision 3 or + /// greater" access permissions set. + /// 4 if the document is encrypted with a V value of 4 + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string R = "/R"; + + /// + /// (Required) A 32-byte string, based on both the owner and user passwords, that is + /// used in computing the encryption key and in determining whether a valid owner + /// password was entered. + /// + [KeyInfo(KeyType.String | KeyType.Required)] + public const string O = "/O"; + + /// + /// (Required) A 32-byte string, based on the user password, that is used in determining + /// whether to prompt the user for a password and, if so, whether a valid user or owner + /// password was entered. + /// + [KeyInfo(KeyType.String | KeyType.Required)] + public const string U = "/U"; + + /// + /// (Required) A set of flags specifying which operations are permitted when the document + /// is opened with user access. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string P = "/P"; + + /// + /// (Optional; meaningful only when the value of V is 4; PDF 1.5) Indicates whether + /// the document-level metadata stream is to be encrypted. Applications should respect this value. + /// Default value: true. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string EncryptMetadata = "/EncryptMetadata"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs new file mode 100644 index 00000000..90150c77 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfDocumentSecurity.cs @@ -0,0 +1,53 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Security +{ + /// + /// Specifies the security level of the PDF document. + /// + public enum PdfDocumentSecurityLevel + { + /// + /// Document is not protected. + /// + None, + + /// + /// Document is protected with 40-bit security. This option is for compatibility with + /// Acrobat 3 and 4 only. Use Encrypted128Bit whenever possible. + /// + Encrypted40Bit, + + /// + /// Document is protected with 128-bit security. + /// + Encrypted128Bit, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfUserAccessPermission.cs b/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfUserAccessPermission.cs new file mode 100644 index 00000000..86bf046f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Security/enums/PdfUserAccessPermission.cs @@ -0,0 +1,92 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf.Security +{ + /// + /// Specifies which operations are permitted when the document is opened with user access. + /// + [Flags] + internal enum PdfUserAccessPermission + { + /// + /// Permits everything. This is the default value. + /// + PermitAll = -3, // = 0xFFFFFFFC, + + // Bit 12 Reserved; must be 0. + + // Bit 3 (Revision 2) Print the document. + // (Revision 3 or greater) Print the document (possibly not at the highest + // quality level, depending on whether bit 12 is also set). + PermitPrint = 0x00000004, //1 << (3 - 1), + + // Bit 4 Modify the contents of the document by operations other than + // those controlled by bits 6, 9, and 11. + PermitModifyDocument = 0x00000008, //1 << (4 - 1), + + // Bit 5 (Revision 2) Copy or otherwise extract text and graphics from the + // document, including extracting text and graphics (in support of accessibility + // to users with disabilities or for other purposes). + // (Revision 3 or greater) Copy or otherwise extract text and graphics + // from the document by operations other than that controlled by bit 10. + PermitExtractContent = 0x00000010, //1 << (5 - 1), + + // Bit 6 Add or modify text annotations, fill in interactive form fields, and, + // if bit 4 is also set, create or modify interactive form fields (including + // signature fields). + PermitAnnotations = 0x00000020, //1 << (6 - 1), + + // Bit 78 Reserved; must be 1. + + // 9 (Revision 3 or greater) Fill in existing interactive form fields (including + // signature fields), even if bit 6 is clear. + PermitFormsFill = 0x00000100, //1 << (9 - 1), + + // Bit 10 (Revision 3 or greater) Extract text and graphics (in support of accessibility + // to users with disabilities or for other purposes). + PermitAccessibilityExtractContent = 0x00000200, //1 << (10 - 1), + + // Bit 11 (Revision 3 or greater) Assemble the document (insert, rotate, or delete + // pages and create bookmarks or thumbnail images), even if bit 4 + // is clear. + PermitAssembleDocument = 0x00000400, //1 << (11 - 1), + + // Bit 12 (Revision 3 or greater) Print the document to a representation from + // which a faithful digital copy of the PDF content could be generated. + // When this bit is clear (and bit 3 is set), printing is limited to a lowlevel + // representation of the appearance, possibly of degraded quality. + // (See implementation note 24 in Appendix H.) + PermitFullQualityPrint = 0x00000800, //1 << (12 - 1), + + //Bit 1332 (Revision 3 or greater) Reserved; must be 1. + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfAttributesBase.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfAttributesBase.cs new file mode 100644 index 00000000..020a1416 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfAttributesBase.cs @@ -0,0 +1,71 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Base class for PDF attributes objects. + /// + public abstract class PdfAttributesBase : PdfDictionary + { + /// + /// Constructor of the abstract class. + /// + /// The document that owns this object. + internal PdfAttributesBase(PdfDocument document) + : base(document) + { } + + /// + /// Constructor of the abstract class. + /// + protected PdfAttributesBase() + { } + + /// + /// Predefined keys of this dictionary. + /// + public class Keys : KeysBase + { + // Reference: TABLE 10.14 Entry common to all attribute object dictionaries / Page 873 + // Reference: TABLE 10.28 Standard attribute owners / Page 914 + + // ReSharper disable InconsistentNaming + + /// + /// (Required) The name of the application or plug-in extension owning the attribute data. + /// The name must conform to the guidelines described in Appendix E + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string O = "/O"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfLayoutAttributes.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfLayoutAttributes.cs new file mode 100644 index 00000000..3b0e0723 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfLayoutAttributes.cs @@ -0,0 +1,88 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents a PDF layout attributes object. + /// + public class PdfLayoutAttributes : PdfAttributesBase + { + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + internal PdfLayoutAttributes(PdfDocument document) + : base(document) + { + SetOwner(); + } + + /// + /// Initializes a new instance of the class. + /// + public PdfLayoutAttributes() + { + SetOwner(); + } + + private void SetOwner() + { + Elements.SetName(PdfAttributesBase.Keys.O, "/Layout"); + } + + /// + /// Predefined keys of this dictionary. + /// + public new class Keys : PdfAttributesBase.Keys + { + // Reference: TABLE 10.28 Standard attribute owners / Page 914 + // Reference: TABLE 10.29 Standard layout attributes / Page 916 + // Reference: TABLE 10.30 Standard layout attributes common to all standard structure types / Page 917 + // Reference: TABLE 10.31 Additional standard layout attributes specific to block-level structure / Page 922 + // Reference: TABLE 10.32 Standard layout attributes specific to inline-level structure elements / Page 926 + // Reference: TABLE 10.33 Standard column attributes / Page 932 + + // ReSharper disable InconsistentNaming + + /// + /// (Optional for Annot; required for any figure or table appearing in its entirety + /// on a single page; not inheritable). An array of four numbers in default user + /// space units giving the coordinates of the left, bottom, right, and top edges, + /// respectively, of the element’s bounding box (the rectangle that completely + /// encloses its visible content). This attribute applies to any element that lies + /// on a single page and occupies a single rectangle. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Optional)] + public const string BBox = "/BBox"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkInformation.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkInformation.cs new file mode 100644 index 00000000..f1c4090f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkInformation.cs @@ -0,0 +1,86 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents a mark information dictionary. + /// + public sealed class PdfMarkInformation : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfMarkInformation() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfMarkInformation(PdfDocument document) + : base(document) + { } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + // Reference: TABLE 10.8 Entries in the mark information dictionary / Page 856 + + // ReSharper disable InconsistentNaming + + /// + /// (Optional) A flag indicating whether the document conforms to Tagged PDF conventions. + /// Default value: false. + /// Note: If Suspects is true, the document may not completely conform to Tagged PDF conventions. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string Marked = "/Marked"; + + /// + /// (Optional; PDF 1.6) A flag indicating the presence of structure elements + /// that contain user properties attributes. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string UserProperties = "/UserProperties"; + + /// + /// (Optional; PDF 1.6) A flag indicating the presence of tag suspects. + /// Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string Suspects = "/Suspects"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkedContentReference.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkedContentReference.cs new file mode 100644 index 00000000..7e3e5401 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfMarkedContentReference.cs @@ -0,0 +1,111 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents a marked-content reference. + /// + public sealed class PdfMarkedContentReference : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfMarkedContentReference() + { + Elements.SetName(Keys.Type, "/MCR"); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfMarkedContentReference(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/MCR"); + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + // Reference: TABLE 10.11 Entries in a marked-content reference dictionary / Page 863 + + // ReSharper disable InconsistentNaming + + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be MCR for a marked-content reference. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "MCR")] + public const string Type = "/Type"; + + /// + /// (Optional; must be an indirect reference) The page object representing + /// the page on which the graphics objects in the marked-content sequence + /// are rendered. This entry overrides any Pg entry in the structure element + /// containing the marked-content reference; + /// it is required if the structure element has no such entry. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Pg = "/Pg"; + + /// + /// (Optional; must be an indirect reference) The content stream containing + /// the marked-content sequence. This entry should be present only if the + /// marked-content sequence resides in a content stream other than the + /// content stream for the pagefor example, in a form XObject or an + /// annotations appearance stream. If this entry is absent, the + /// marked-content sequence is contained in the content stream of the page + /// identified by Pg (either in the marked-content reference dictionary or + /// in the parent structure element). + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string Stm = "/Stm"; + + /// + /// (Optional; must be an indirect reference) The PDF object owning the stream + /// identified by Stmfor example, the annotation to which an appearance stream belongs. + /// + [KeyInfo(KeyType.Optional)] + public const string StmOwn = "/StmOwn"; + + /// + /// (Required) The marked-content identifier of the marked-content sequence + /// within its content stream. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string MCID = "/MCID"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfObjectReference.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfObjectReference.cs new file mode 100644 index 00000000..aaf4a7d5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfObjectReference.cs @@ -0,0 +1,89 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents a marked-content reference. + /// + public sealed class PdfObjectReference : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfObjectReference() + { + Elements.SetName(Keys.Type, "/OBJR"); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfObjectReference(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/OBJR"); + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + // Reference: TABLE 10.12 Entries in an object reference dictionary / Page 868 + + // ReSharper disable InconsistentNaming + + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be OBJR for an object reference. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "OBJR")] + public const string Type = "/Type"; + + /// + /// (Optional; must be an indirect reference) The page object representing the page + /// on which the object is rendered. This entry overrides any Pg entry in the + /// structure element containing the object reference; + /// it is required if the structure element has no such entry. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Pg = "/Pg"; + + /// + /// (Required; must be an indirect reference) The referenced object. + /// + [KeyInfo(KeyType.Required)] + public const string Obj = "/Obj"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureElement.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureElement.cs new file mode 100644 index 00000000..3df60032 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureElement.cs @@ -0,0 +1,337 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents the root of a structure tree. + /// + public sealed class PdfStructureElement : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfStructureElement() + { + Elements.SetName(Keys.Type, "/StructElem"); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfStructureElement(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/StructElem"); + } + + internal override void PrepareForSave() + { + SimplifyKidsArray(); + SimplifyAttributes(); + + foreach (var k in GetKids(Elements)) + k.PrepareForSave(); + } + + /// + /// Returns all PdfDictionaries saved in the "/K" key. + /// + internal static IEnumerable GetKids(DictionaryElements elements) + { + if (elements != null) + { + var k = elements.GetObject(Keys.K); + + var array = k as PdfArray; + // If k is holding an array, return all elements. + if (array != null) + { + foreach (var item in array) + { + var dict = GetPdfDictionary(item); + if (dict != null) + yield return dict; + } + } + else + { + var dict = GetPdfDictionary(k); + if (dict != null) + yield return dict; + } + } + } + + /// + /// Returns the PdfDictionary that is lying direct or inderect in "item". + /// + static PdfDictionary GetPdfDictionary(PdfItem item) + { + var r = item as PdfReference; + if (r != null) + return r.Value as PdfDictionary; + return item as PdfDictionary; + } + + /// + /// Removes the array and directly adds its first item, if there is only one item. + /// + void SimplifyKidsArray() + { + var k = Elements[Keys.K] as PdfArray; + if (k != null && k.Elements.Count == 1) + { + var item = k.Elements[0]; + Elements[Keys.K] = item; + } + } + + /// + /// Removes unnessecary Attribute dictionaries or arrays. + /// + void SimplifyAttributes() + { + var a = Elements[Keys.A]; + + var array = a as PdfArray; + if (array != null) + { + // Remove attribute dictionaries that don't contain relevant entries. + for (var i = 0; i < array.Elements.Count; i++) + { + var dict = GetPdfDictionary(array.Elements[i]); + if (dict != null && AttributeDictionaryIsEmpty(dict)) + array.Elements.RemoveAt(i--); + } + + // Remove the array and directly add its first item, if there is only one item. + if (array.Elements.Count == 1) + { + var item = array.Elements[0]; + Elements[Keys.A] = item; + } + // Remove the key, if the array is empty. + else if (array.Elements.Count == 0) + Elements.Remove(Keys.A); + } + else + { + // Remove the attribute dictionary, if it doesn't contain relevant entries. + var dict = GetPdfDictionary(a); + if (dict != null && AttributeDictionaryIsEmpty(dict)) + Elements.Remove(Keys.A); + } + } + + bool AttributeDictionaryIsEmpty(PdfDictionary dictionary) + { + if (dictionary.Elements.Count == 0) + return true; + // Also return true, if the only entry is the required "/O". + if (dictionary.Elements.Count == 1 && dictionary.Elements.ContainsKey(PdfAttributesBase.Keys.O)) + return true; + return false; + } + + /// + /// Gets the PdfLayoutAttributes instance in "/A". If not existing it creates one. + /// + public PdfLayoutAttributes LayoutAttributes + { + get { return GetAttributes(); } + } + + /// + /// Gets the PdfTableAttributes instance in "/A". If not existing it creates one. + /// + public PdfTableAttributes TableAttributes + { + get { return GetAttributes(); } + } + + T GetAttributes() where T : PdfAttributesBase, new() + { + var a = Elements[Keys.A]; + var array = a as PdfArray; + if (array == null) + { + // If there is no PdfArray saved in "/A", create one. + array = new PdfArray(Owner); + // If there is anything saved in "/A", move it into the array. + if (a != null) + array.Elements.Add(a); + Elements.SetObject(Keys.A, array); + } + + // Return the first instance of T in the array. + for (var i = 0; i < array.Elements.Count; i++) + { + var c = array.Elements[i]; + if (c is T) + return c as T; + } + + // Create and add a new instance of T, if there's no one. + var t = new T { Document = Owner }; + array.Elements.Add(t); + return t; + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + // Reference: TABLE 10.10 Entries in a structure element dictionary / Page 858 + + // ReSharper disable InconsistentNaming + + /// + /// (Optional) The type of PDF object that this dictionary describes; + /// if present, must be StructElem for a structure element. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "StructElem")] + public const string Type = "/Type"; + + /// + /// (Required) The structure type, a name object identifying the nature of the + /// structure element and its role within the document, such as a chapter, + /// paragraph, or footnote. + /// Names of structure types must conform to the guidelines described in Appendix E. + /// + [KeyInfo(KeyType.Name | KeyType.Required)] + public const string S = "/S"; + + /// + /// (Required; must be an indirect reference) The structure element that + /// is the immediate parent of this one in the structure hierarchy. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string P = "/P"; + + /// + /// (Optional) The element identifier, a byte string designating this structure element. + /// The string must be unique among all elements in the documents structure hierarchy. + /// The IDTree entry in the structure tree root defines the correspondence between + /// element identifiers and the structure elements they denote. + /// + [KeyInfo(KeyType.ByteString | KeyType.Optional)] + public const string ID = "/ID"; + + /// + /// (Optional; must be an indirect reference) A page object representing a page on + /// which some or all of the content items designated by the K entry are rendered. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string Pg = "/Pg"; + + /// + /// (Optional) The children of this structure element. The value of this entry + /// may be one of the following objects or an array consisting of one or more + /// of the following objects: + /// A structure element dictionary denoting another structure element + /// An integer marked-content identifier denoting a marked-content sequence + /// A marked-content reference dictionary denoting a marked-content sequence + /// An object reference dictionary denoting a PDF object + /// Each of these objects other than the first (structure element dictionary) + /// is considered to be a content item. + /// Note: If the value of K is a dictionary containing no Type entry, + /// it is assumed to be a structure element dictionary. + /// + [KeyInfo(KeyType.Various | KeyType.Optional)] + public const string K = "/K"; + + /// + /// (Optional) A single attribute object or array of attribute objects associated + /// with this structure element. Each attribute object is either a dictionary or + /// a stream. If the value of this entry is an array, each attribute object in + /// the array may be followed by an integer representing its revision number. + /// + [KeyInfo(KeyType.Various | KeyType.Optional)] + public const string A = "/A"; + + /// + /// (Optional) An attribute class name or array of class names associated with this + /// structure element. If the value of this entry is an array, each class name in the + /// array may be followed by an integer representing its revision number. + /// Note: If both the A and C entries are present and a given attribute is specified + /// by both, the one specified by the A entry takes precedence. + /// + [KeyInfo(KeyType.Various | KeyType.Optional)] + public const string C = "/C"; + + /// + /// (Optional) The title of the structure element, a text string representing it in + /// human-readable form. The title should characterize the specific structure element, + /// such as Chapter 1, rather than merely a generic element type, such as Chapter. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string T = "/T"; + + /// + /// (Optional; PDF 1.4) A language identifier specifying the natural language + /// for all text in the structure element except where overridden by language + /// specifications for nested structure elements or marked content. + /// If this entry is absent, the language (if any) specified in the document catalog applies. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Lang = "/Lang"; + + /// + /// (Optional) An alternate description of the structure element and its children + /// in human-readable form, which is useful when extracting the documents contents + /// in support of accessibility to users with disabilities or for other purposes. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string Alt = "/Alt"; + + /// + /// (Optional; PDF 1.5) The expanded form of an abbreviation. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string E = "/E"; + + /// + /// (Optional; PDF 1.4) Text that is an exact replacement for the structure element and + /// its children. This replacement text (which should apply to as small a piece of + /// content as possible) is useful when extracting the documents contents in support + /// of accessibility to users with disabilities or for other purposes. + /// + [KeyInfo(KeyType.TextString | KeyType.Optional)] + public const string ActualText = "/ActualText"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureTreeRoot.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureTreeRoot.cs new file mode 100644 index 00000000..f3c6fe37 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfStructureTreeRoot.cs @@ -0,0 +1,136 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents the root of a structure tree. + /// + public sealed class PdfStructureTreeRoot : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfStructureTreeRoot() + { + Elements.SetName(Keys.Type, "/StructTreeRoot"); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfStructureTreeRoot(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/StructTreeRoot"); + } + + internal override void PrepareForSave() + { + foreach (var k in PdfStructureElement.GetKids(Elements)) + k.PrepareForSave(); + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + // Reference: TABLE 10.9 Entries in the structure tree root / Page 857 + + // ReSharper disable InconsistentNaming + + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be StructTreeRoot for a structure tree root. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "StructTreeRoot")] + public const string Type = "/Type"; + + /// + /// (Optional) The immediate child or children of the structure tree root + /// in the structure hierarchy. The value may be either a dictionary + /// representing a single structure element or an array of such dictionaries. + /// + [KeyInfo(KeyType.ArrayOrDictionary | KeyType.Optional)] + public const string K = "/K"; + + /// + /// (Required if any structure elements have element identifiers) + /// A name tree that maps element identifiers to the structure elements they denote. + /// + [KeyInfo(KeyType.Optional)] + public const string IDTree = "/IDTree"; + + /// + /// (Required if any structure element contains content items) A number tree + /// used in finding the structure elements to which content items belong. + /// Each integer key in the number tree corresponds to a single page of the + /// document or to an individual object (such as an annotation or an XObject) + /// that is a content item in its own right. The integer key is given as the + /// value of the StructParent or StructParents entry in that object. + /// The form of the associated value depends on the nature of the object: + /// For an object that is a content item in its own right, the value is an + /// indirect reference to the objects parent element (the structure element + /// that contains it as a content item). + /// For a page object or content stream containing marked-content sequences + /// that are content items, the value is an array of references to the parent + /// elements of those marked-content sequences. + /// + [KeyInfo(KeyType.NumberTree | KeyType.Optional)] + public const string ParentTree = "/ParentTree"; + + /// + /// (Optional) An integer greater than any key in the parent tree, to be used as a + /// key for the next entry added to the tree. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string ParentTreeNextKey = "/ParentTreeNextKey"; + + /// + /// (Optional) A dictionary that maps the names of structure types used in the + /// document to their approximate equivalents in the set of standard structure types. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string RoleMap = "/RoleMap"; + + /// + /// (Optional) A dictionary that maps name objects designating attribute + /// classes to the corresponding attribute objects or arrays of attribute objects. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string ClassMap = "/ClassMap"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfTableAttributes.cs b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfTableAttributes.cs new file mode 100644 index 00000000..208c4288 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf.Structure/PdfTableAttributes.cs @@ -0,0 +1,92 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf.Structure +{ + /// + /// Represents a PDF table attributes object. + /// + public class PdfTableAttributes : PdfAttributesBase + { + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + internal PdfTableAttributes(PdfDocument document) + : base(document) + { + SetOwner(); + } + + /// + /// Initializes a new instance of the class. + /// + public PdfTableAttributes() + { + SetOwner(); + } + + private void SetOwner() + { + Elements.SetName(PdfAttributesBase.Keys.O, "/Table"); + } + + /// + /// Predefined keys of this dictionary. + /// + public new class Keys : PdfAttributesBase.Keys + { + // Reference: TABLE 10.36 Standard table attributes / Page 935 + + // ReSharper disable InconsistentNaming + + /// + /// (Optional; not inheritable) The number of rows in the enclosing table that are spanned + /// by the cell. The cell expands by adding rows in the block-progression direction + /// specified by the table’s WritingMode attribute. Default value: 1. + /// This entry applies only to table cells that have structure types TH or TD or that are + /// role mapped to structure types TH or TD. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string RowSpan = "/RowSpan"; + + /// + /// (Optional; not inheritable) The number of columns in the enclosing table that are spanned + /// by the cell. The cell expands by adding columns in the inline-progression direction + /// specified by the table’s WritingMode attribute. Default value: 1. + /// This entry applies only to table cells that have structure types TH or TD or that are + /// role mapped to structure types TH or TD. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string ColSpan = "/ColSpan"; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/EntryInfoAttribute.cs b/src/PDFsharp/src/PdfSharp/Pdf/EntryInfoAttribute.cs new file mode 100644 index 00000000..84995662 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/EntryInfoAttribute.cs @@ -0,0 +1,137 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the type of a key's value in a dictionary. + /// + [Flags] + internal enum KeyType + { + Name = 0x00000001, + String = 0x00000002, + Boolean = 0x00000003, + Integer = 0x00000004, + Real = 0x00000005, + Date = 0x00000006, + Rectangle = 0x00000007, + Array = 0x00000008, + Dictionary = 0x00000009, + Stream = 0x0000000A, + NumberTree = 0x0000000B, + Function = 0x0000000C, + TextString = 0x0000000D, + ByteString = 0x0000000E, + NameTree = 0x0000000F, + FileSpecification = 0x00000010, + + NameOrArray = 0x00000100, + NameOrDictionary = 0x00000200, + ArrayOrDictionary = 0x00000300, + StreamOrArray = 0x00000400, + StreamOrName = 0x00000500, + ArrayOrNameOrString = 0x00000600, + FunctionOrName = 0x000000700, + Various = 0x000000800, + + TypeMask = 0x00000FFF, + + Optional = 0x00001000, + Required = 0x00002000, + Inheritable = 0x00004000, + MustBeIndirect = 0x00010000, + MustNotBeIndirect = 0x00020000, + } + + /// + /// Summary description for KeyInfo. + /// + internal class KeyInfoAttribute : Attribute + { + public KeyInfoAttribute() + { } + + public KeyInfoAttribute(KeyType keyType) + { + //_version = version; + KeyType = keyType; + } + + public KeyInfoAttribute(string version, KeyType keyType) + { + _version = version; + KeyType = keyType; + } + + public KeyInfoAttribute(KeyType keyType, Type objectType) + { + //_version = version; + KeyType = keyType; + _objectType = objectType; + } + + public KeyInfoAttribute(string version, KeyType keyType, Type objectType) + { + //_version = version; + KeyType = keyType; + _objectType = objectType; + } + + public string Version + { + get { return _version; } + set { _version = value; } + } + string _version = "1.0"; + + public KeyType KeyType + { + get { return _entryType; } + set { _entryType = value; } + } + KeyType _entryType; + + public Type ObjectType + { + get { return _objectType; } + set { _objectType = value; } + } + Type _objectType; + + public string FixedValue + { + get { return _fixedValue; } + set { _fixedValue = value; } + } + string _fixedValue; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/KeysBase.cs b/src/PDFsharp/src/PdfSharp/Pdf/KeysBase.cs new file mode 100644 index 00000000..42fe999a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/KeysBase.cs @@ -0,0 +1,44 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Base class for all dictionary Keys classes. + /// + public class KeysBase + { + internal static DictionaryMeta CreateMeta(Type type) + { + return new DictionaryMeta(type); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/KeysMeta.cs b/src/PDFsharp/src/PdfSharp/Pdf/KeysMeta.cs new file mode 100644 index 00000000..d6d383cc --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/KeysMeta.cs @@ -0,0 +1,311 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections.Generic; +using System.Reflection; +using PdfSharp.Pdf.Advanced; + +namespace PdfSharp.Pdf +{ + /// + /// Holds information about the value of a key in a dictionary. This information is used to create + /// and interpret this value. + /// + internal sealed class KeyDescriptor + { + /// + /// Initializes a new instance of KeyDescriptor from the specified attribute during a KeysMeta + /// initializes itself using reflection. + /// + public KeyDescriptor(KeyInfoAttribute attribute) + { + _version = attribute.Version; + _keyType = attribute.KeyType; + _fixedValue = attribute.FixedValue; + _objectType = attribute.ObjectType; + + if (_version == "") + _version = "1.0"; + } + + /// + /// Gets or sets the PDF version starting with the availability of the described key. + /// + public string Version + { + get { return _version; } + set { _version = value; } + } + string _version; + + public KeyType KeyType + { + get { return _keyType; } + set { _keyType = value; } + } + KeyType _keyType; + + public string KeyValue + { + get { return _keyValue; } + set { _keyValue = value; } + } + string _keyValue; + + public string FixedValue + { + get { return _fixedValue; } + } + readonly string _fixedValue; + + public Type ObjectType + { + get { return _objectType; } + set { _objectType = value; } + } + Type _objectType; + + public bool CanBeIndirect + { + get { return (_keyType & KeyType.MustNotBeIndirect) == 0; } + } + + /// + /// Returns the type of the object to be created as value for the described key. + /// + public Type GetValueType() + { + Type type = _objectType; + if (type == null) + { + // If we have no ObjectType specified, use the KeyType enumeration. + switch (_keyType & KeyType.TypeMask) + { + case KeyType.Name: + type = typeof(PdfName); + break; + + case KeyType.String: + type = typeof(PdfString); + break; + + case KeyType.Boolean: + type = typeof(PdfBoolean); + break; + + case KeyType.Integer: + type = typeof(PdfInteger); + break; + + case KeyType.Real: + type = typeof(PdfReal); + break; + + case KeyType.Date: + type = typeof(PdfDate); + break; + + case KeyType.Rectangle: + type = typeof(PdfRectangle); + break; + + case KeyType.Array: + type = typeof(PdfArray); + break; + + case KeyType.Dictionary: + type = typeof(PdfDictionary); + break; + + case KeyType.Stream: + type = typeof(PdfDictionary); + break; + + case KeyType.NumberTree: + type = typeof(PdfNumberTreeNode); + break; + + case KeyType.NameTree: + type = typeof(PdfNameTreeNode); + break; + + case KeyType.FileSpecification: + type = typeof(PdfFileSpecification); + break; + + // The following types are not yet used + + case KeyType.NameOrArray: + throw new NotImplementedException("KeyType.NameOrArray"); + + case KeyType.ArrayOrDictionary: + throw new NotImplementedException("KeyType.ArrayOrDictionary"); + + case KeyType.StreamOrArray: + throw new NotImplementedException("KeyType.StreamOrArray"); + + case KeyType.ArrayOrNameOrString: + return null; // HACK: Make PdfOutline work + //throw new NotImplementedException("KeyType.ArrayOrNameOrString"); + + default: + Debug.Assert(false, "Invalid KeyType: " + _keyType); + break; + } + } + return type; + } + } + + /// + /// Contains meta information about all keys of a PDF dictionary. + /// + internal class DictionaryMeta + { + public DictionaryMeta(Type type) + { + //#if (NETFX_CORE && DEBUG) || CORE + // if (type == typeof(PdfPages.Keys)) + // { + // var x = typeof(PdfPages).GetRuntimeFields(); + // var y = typeof(PdfPages).GetTypeInfo().DeclaredFields; + // x.GetType(); + // y.GetType(); + // Debug-Break.Break(); + // Test.It(); + // } + //#endif +#if !NETFX_CORE && !UWP + FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); + foreach (FieldInfo field in fields) + { + object[] attributes = field.GetCustomAttributes(typeof(KeyInfoAttribute), false); + if (attributes.Length == 1) + { + KeyInfoAttribute attribute = (KeyInfoAttribute)attributes[0]; + KeyDescriptor descriptor = new KeyDescriptor(attribute); + descriptor.KeyValue = (string)field.GetValue(null); + _keyDescriptors[descriptor.KeyValue] = descriptor; + } + } +#else + // Rewritten for WinRT. + CollectKeyDescriptors(type); + //var fields = type.GetRuntimeFields(); // does not work + //fields2.GetType(); + //foreach (FieldInfo field in fields) + //{ + // var attributes = field.GetCustomAttributes(typeof(KeyInfoAttribute), false); + // foreach (var attribute in attributes) + // { + // KeyDescriptor descriptor = new KeyDescriptor((KeyInfoAttribute)attribute); + // descriptor.KeyValue = (string)field.GetValue(null); + // _keyDescriptors[descriptor.KeyValue] = descriptor; + // } + //} +#endif + } + +#if NETFX_CORE || UWP || DNC10 + // Background: The function GetRuntimeFields gets constant fields only for the specified type, + // not for its base types. So we have to walk recursively through base classes. + // The docmentation says full trust for the immediate caller is required for property BaseClass. + // TODO: Rewrite this stuff for medium trust. + void CollectKeyDescriptors(Type type) + { + // Get fields of the specified type only. + var fields = type.GetTypeInfo().DeclaredFields; + foreach (FieldInfo field in fields) + { + var attributes = field.GetCustomAttributes(typeof(KeyInfoAttribute), false); + foreach (var attribute in attributes) + { + KeyDescriptor descriptor = new KeyDescriptor((KeyInfoAttribute)attribute); + descriptor.KeyValue = (string)field.GetValue(null); + _keyDescriptors[descriptor.KeyValue] = descriptor; + } + } + type = type.GetTypeInfo().BaseType; + if (type != typeof(object) && type != typeof(PdfObject)) + CollectKeyDescriptors(type); + } +#endif + +#if (NETFX_CORE || CORE) && true_ + public class A + { + public string _a; + public const string _ca = "x"; + } + public class B : A + { + public string _b; + public const string _cb = "x"; + + void Foo() + { + var str = A._ca; + } + } + class Test + { + public static void It() + { + string s = "Runtime fields of B:"; + foreach (var fieldInfo in typeof(B).GetRuntimeFields()) { s += " " + fieldInfo.Name; } + Debug.WriteLine(s); + + s = "Declared fields of B:"; + foreach (var fieldInfo in typeof(B).GetTypeInfo().DeclaredFields) { s += " " + fieldInfo.Name; } + Debug.WriteLine(s); + + s = "Runtime fields of PdfPages.Keys:"; + foreach (var fieldInfo in typeof(PdfPages.Keys).GetRuntimeFields()) { s += " " + fieldInfo.Name; } + Debug.WriteLine(s); + } + } +#endif + /// + /// Gets the KeyDescriptor of the specified key, or null if no such descriptor exits. + /// + public KeyDescriptor this[string key] + { + get + { + KeyDescriptor keyDescriptor; + _keyDescriptors.TryGetValue(key, out keyDescriptor); + return keyDescriptor; + } + } + + readonly Dictionary _keyDescriptors = new Dictionary(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfArray.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfArray.cs new file mode 100644 index 00000000..2c278b6c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfArray.cs @@ -0,0 +1,630 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Collections; +using System.Globalization; +using System.Text; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a PDF array object. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public class PdfArray : PdfObject, IEnumerable + { + /// + /// Initializes a new instance of the class. + /// + public PdfArray() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfArray(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + /// The items. + public PdfArray(PdfDocument document, params PdfItem[] items) + : base(document) + { + foreach (PdfItem item in items) + Elements.Add(item); + } + + /// + /// Initializes a new instance from an existing dictionary. Used for object type transformation. + /// + /// The array. + protected PdfArray(PdfArray array) + : base(array) + { + if (array._elements != null) + array._elements.ChangeOwner(this); + } + + /// + /// Creates a copy of this array. Direct elements are deep copied. + /// Indirect references are not modified. + /// + public new PdfArray Clone() + { + return (PdfArray)Copy(); + } + + /// + /// Implements the copy mechanism. + /// + protected override object Copy() + { + PdfArray array = (PdfArray)base.Copy(); + if (array._elements != null) + { + array._elements = array._elements.Clone(); + int count = array._elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = array._elements[idx]; + if (item is PdfObject) + array._elements[idx] = item.Clone(); + } + } + return array; + } + + /// + /// Gets the collection containing the elements of this object. + /// + public ArrayElements Elements + { + get { return _elements ?? (_elements = new ArrayElements(this)); } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + public virtual IEnumerator GetEnumerator() + { + return Elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns a string with the content of this object in a readable form. Useful for debugging purposes only. + /// + public override string ToString() + { + StringBuilder pdf = new StringBuilder(); + pdf.Append("[ "); + int count = Elements.Count; + for (int idx = 0; idx < count; idx++) + pdf.Append(Elements[idx] + " "); + pdf.Append("]"); + return pdf.ToString(); + } + + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + int count = Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem value = Elements[idx]; + value.WriteObject(writer); + } + writer.WriteEndObject(); + } + + /// + /// Represents the elements of an PdfArray. + /// + public sealed class ArrayElements : IList, ICloneable + { + internal ArrayElements(PdfArray array) + { + _elements = new List(); + _ownerArray = array; + } + + object ICloneable.Clone() + { + ArrayElements elements = (ArrayElements)MemberwiseClone(); + elements._elements = new List(elements._elements); + elements._ownerArray = null; + return elements; + } + + /// + /// Creates a shallow copy of this object. + /// + public ArrayElements Clone() + { + return (ArrayElements)((ICloneable)this).Clone(); + } + + /// + /// Moves this instance to another array during object type transformation. + /// + internal void ChangeOwner(PdfArray array) + { + if (_ownerArray != null) + { + // ??? + } + + // Set new owner. + _ownerArray = array; + + // Set owners elements to this. + array._elements = this; + } + + /// + /// Converts the specified value to boolean. + /// If the value does not exist, the function returns false. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public bool GetBoolean(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return false; + + PdfBoolean boolean = obj as PdfBoolean; + if (boolean != null) + return boolean.Value; + + PdfBooleanObject booleanObject = obj as PdfBooleanObject; + if (booleanObject != null) + return booleanObject.Value; + + throw new InvalidCastException("GetBoolean: Object is not a boolean."); + } + + /// + /// Converts the specified value to integer. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public int GetInteger(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return 0; + + PdfInteger integer = obj as PdfInteger; + if (integer != null) + return integer.Value; + + PdfIntegerObject integerObject = obj as PdfIntegerObject; + if (integerObject != null) + return integerObject.Value; + + throw new InvalidCastException("GetInteger: Object is not an integer."); + } + + /// + /// Converts the specified value to double. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public double GetReal(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return 0; + + PdfReal real = obj as PdfReal; + if (real != null) + return real.Value; + + PdfRealObject realObject = obj as PdfRealObject; + if (realObject != null) + return realObject.Value; + + PdfInteger integer = obj as PdfInteger; + if (integer != null) + return integer.Value; + + PdfIntegerObject integerObject = obj as PdfIntegerObject; + if (integerObject != null) + return integerObject.Value; + + throw new InvalidCastException("GetReal: Object is not a number."); + } + + /// + /// Converts the specified value to double?. + /// If the value does not exist, the function returns null. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public double? GetNullableReal(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return null; + + PdfNull @null = obj as PdfNull; + if (@null != null) + return null; + + PdfNullObject nullObject = obj as PdfNullObject; + if (nullObject != null) + return null; + + PdfReal real = obj as PdfReal; + if (real != null) + return real.Value; + + PdfRealObject realObject = obj as PdfRealObject; + if (realObject != null) + return realObject.Value; + + PdfInteger integer = obj as PdfInteger; + if (integer != null) + return integer.Value; + + PdfIntegerObject integerObject = obj as PdfIntegerObject; + if (integerObject != null) + return integerObject.Value; + + throw new InvalidCastException("GetReal: Object is not a number."); + } + + /// + /// Converts the specified value to string. + /// If the value does not exist, the function returns the empty string. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public string GetString(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return String.Empty; + + PdfString str = obj as PdfString; + if (str != null) + return str.Value; + + PdfStringObject strObject = obj as PdfStringObject; + if (strObject != null) + return strObject.Value; + + throw new InvalidCastException("GetString: Object is not a string."); + } + + /// + /// Converts the specified value to a name. + /// If the value does not exist, the function returns the empty string. + /// If the value is not convertible, the function throws an InvalidCastException. + /// If the index is out of range, the function throws an ArgumentOutOfRangeException. + /// + public string GetName(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + object obj = this[index]; + if (obj == null) + return String.Empty; + + PdfName name = obj as PdfName; + if (name != null) + return name.Value; + + PdfNameObject nameObject = obj as PdfNameObject; + if (nameObject != null) + return nameObject.Value; + + throw new InvalidCastException("GetName: Object is not a name."); + } + + /// + /// Returns the indirect object if the value at the specified index is a PdfReference. + /// + [Obsolete("Use GetObject, GetDictionary, GetArray, or GetReference")] + public PdfObject GetIndirectObject(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + PdfReference reference = this[index] as PdfReference; + if (reference != null) + return reference.Value; + + return null; + } + + /// + /// Gets the PdfObject with the specified index, or null, if no such object exists. If the index refers to + /// a reference, the referenced PdfObject is returned. + /// + public PdfObject GetObject(int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.IndexOutOfRange); + + PdfItem item = this[index]; + PdfReference reference = item as PdfReference; + if (reference != null) + return reference.Value; + + return item as PdfObject; + } + + /// + /// Gets the PdfArray with the specified index, or null, if no such object exists. If the index refers to + /// a reference, the referenced PdfArray is returned. + /// + public PdfDictionary GetDictionary(int index) + { + return GetObject(index) as PdfDictionary; + } + + /// + /// Gets the PdfArray with the specified index, or null, if no such object exists. If the index refers to + /// a reference, the referenced PdfArray is returned. + /// + public PdfArray GetArray(int index) + { + return GetObject(index) as PdfArray; + } + + /// + /// Gets the PdfReference with the specified index, or null, if no such object exists. + /// + public PdfReference GetReference(int index) + { + PdfItem item = this[index]; + return item as PdfReference; + } + + /// + /// Gets all items of this array. + /// + public PdfItem[] Items + { + get { return _elements.ToArray(); } + } + + #region IList Members + + /// + /// Returns false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Gets or sets an item at the specified index. + /// + /// + public PdfItem this[int index] + { + get { return _elements[index]; } + set + { + if (value == null) + throw new ArgumentNullException("value"); + _elements[index] = value; + } + } + + /// + /// Removes the item at the specified index. + /// + public void RemoveAt(int index) + { + _elements.RemoveAt(index); + } + + /// + /// Removes the first occurrence of a specific object from the array/>. + /// + public bool Remove(PdfItem item) + { + return _elements.Remove(item); + } + + /// + /// Inserts the item the specified index. + /// + public void Insert(int index, PdfItem value) + { + _elements.Insert(index, value); + } + + /// + /// Determines whether the specified value is in the array. + /// + public bool Contains(PdfItem value) + { + return _elements.Contains(value); + } + + /// + /// Removes all items from the array. + /// + public void Clear() + { + _elements.Clear(); + } + + /// + /// Gets the index of the specified item. + /// + public int IndexOf(PdfItem value) + { + return _elements.IndexOf(value); + } + + /// + /// Appends the specified object to the array. + /// + public void Add(PdfItem value) + { + // TODO: ??? + //Debug.Assert((value is PdfObject && ((PdfObject)value).Reference == null) | !(value is PdfObject), + // "You try to set an indirect object directly into an array."); + + PdfObject obj = value as PdfObject; + if (obj != null && obj.IsIndirect) + _elements.Add(obj.Reference); + else + _elements.Add(value); + } + + /// + /// Returns false. + /// + public bool IsFixedSize + { + get { return false; } + } + + #endregion + + #region ICollection Members + + /// + /// Returns false. + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + /// Gets the number of elements in the array. + /// + public int Count + { + get { return _elements.Count; } + } + + /// + /// Copies the elements of the array to the specified array. + /// + public void CopyTo(PdfItem[] array, int index) + { + _elements.CopyTo(array, index); + } + + /// + /// The current implementation return null. + /// + public object SyncRoot + { + get { return null; } + } + + #endregion + + /// + /// Returns an enumerator that iterates through the array. + /// + public IEnumerator GetEnumerator() + { + return _elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _elements.GetEnumerator(); + } + + /// + /// The elements of the array. + /// + List _elements; + + /// + /// The array this objects belongs to. + /// + PdfArray _ownerArray; + } + + ArrayElements _elements; + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { +#if true + return String.Format(CultureInfo.InvariantCulture, "array({0},[{1}])", ObjectID.DebuggerDisplay, _elements == null ? 0 : _elements.Count); +#else + return String.Format(CultureInfo.InvariantCulture, "array({0},[{1}])", ObjectID.DebuggerDisplay, _elements == null ? 0 : _elements.Count); +#endif + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfBoolean.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfBoolean.cs new file mode 100644 index 00000000..140bbaba --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfBoolean.cs @@ -0,0 +1,91 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a direct boolean value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfBoolean : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + public PdfBoolean() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfBoolean(bool value) + { + _value = value; + } + + /// + /// Gets the value of this instance as boolean value. + /// + public bool Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly bool _value; + + /// + /// A pre-defined value that represents true. + /// + public static readonly PdfBoolean True = new PdfBoolean(true); + + /// + /// A pre-defined value that represents false. + /// + public static readonly PdfBoolean False = new PdfBoolean(false); + + /// + /// Returns 'false' or 'true'. + /// + public override string ToString() + { + return _value ? bool.TrueString : bool.FalseString; + } + + /// + /// Writes 'true' or 'false'. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfBooleanObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfBooleanObject.cs new file mode 100644 index 00000000..5b739b58 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfBooleanObject.cs @@ -0,0 +1,94 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect boolean value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfBooleanObject : PdfObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfBooleanObject() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfBooleanObject(bool value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public PdfBooleanObject(PdfDocument document, bool value) + : base(document) + { + _value = value; + } + + /// + /// Gets the value of this instance as boolean value. + /// + public bool Value + { + get { return _value; } + //set { _value = value; } + } + + readonly bool _value; + + /// + /// Returns "false" or "true". + /// + public override string ToString() + { + return _value ? bool.TrueString : bool.FalseString; + } + + /// + /// Writes the keyword false or true. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(_value); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValue.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValue.cs new file mode 100644 index 00000000..c2554c02 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValue.cs @@ -0,0 +1,79 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// This class is intended for empira internal use only and may change or drop in future releases. + /// + public class PdfCustomValue : PdfDictionary + { + /// + /// This function is intended for empira internal use only. + /// + public PdfCustomValue() + { + CreateStream(new byte[] { }); + } + + /// + /// This function is intended for empira internal use only. + /// + public PdfCustomValue(byte[] bytes) + { + CreateStream(bytes); + } + + internal PdfCustomValue(PdfDocument document) + : base(document) + { + CreateStream(new byte[] { }); + } + + internal PdfCustomValue(PdfDictionary dict) + : base(dict) + { + // TODO: uncompress stream + } + + /// + /// This property is intended for empira internal use only. + /// + public PdfCustomValueCompressionMode CompressionMode; + + /// + /// This property is intended for empira internal use only. + /// + public byte[] Value + { + get { return Stream.Value; } + set { Stream.Value = value; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValues.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValues.cs new file mode 100644 index 00000000..16e2bfa5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfCustomValues.cs @@ -0,0 +1,161 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// This class is intended for empira internal use only and may change or drop in future releases. + /// + public class PdfCustomValues : PdfDictionary + { + internal PdfCustomValues() + { } + + internal PdfCustomValues(PdfDocument document) + : base(document) + { } + + internal PdfCustomValues(PdfDictionary dict) + : base(dict) + { } + + /// + /// This function is intended for empira internal use only. + /// + public PdfCustomValueCompressionMode CompressionMode + { + set { throw new NotImplementedException(); } + } + + /// + /// This function is intended for empira internal use only. + /// + public bool Contains(string key) + { + return Elements.ContainsKey(key); + } + + /// + /// This function is intended for empira internal use only. + /// + public PdfCustomValue this[string key] + { + get + { + PdfDictionary dict = Elements.GetDictionary(key); + if (dict == null) + return null; + PdfCustomValue cust = dict as PdfCustomValue; + if (cust == null) + cust = new PdfCustomValue(dict); + return cust; + } + set + { + if (value == null) + { + Elements.Remove(key); + } + else + { + Owner.Internals.AddObject(value); + Elements.SetReference(key, value); + } + } +#if old + get + { + PdfDictionary dict = Elements.GetDictionary(key); + if (dict == null) + return null; + if (!(dict is PdfCustomValue)) + dict = new PdfCustomValue(dict); + return dict.Stream.Value; + } + set + { + PdfCustomValue cust; + PdfDictionary dict = Elements.GetDictionary(key); + if (dict == null) + { + cust = new PdfCustomValue(); + Owner.Internals.AddObject(cust); + Elements.Add(key, cust); + } + else + { + cust = dict as PdfCustomValue; + if (cust == null) + cust = new PdfCustomValue(dict); + } + cust.Value = value; + } +#endif + } + + /// + /// This function is intended for empira internal use only. + /// + public static void ClearAllCustomValues(PdfDocument document) + { + document.CustomValues = null; + foreach (PdfPage page in document.Pages) + page.CustomValues = null; + } + + //public static string Key = "/PdfSharp.CustomValue"; + + internal static PdfCustomValues Get(DictionaryElements elem) + { + string key = elem.Owner.Owner.Internals.CustomValueKey; + PdfCustomValues customValues; + PdfDictionary dict = elem.GetDictionary(key); + if (dict == null) + { + customValues = new PdfCustomValues(); + elem.Owner.Owner.Internals.AddObject(customValues); + elem.Add(key, customValues); + } + else + { + customValues = dict as PdfCustomValues; + if (customValues == null) + customValues = new PdfCustomValues(dict); + } + return customValues; + } + + internal static void Remove(DictionaryElements elem) + { + elem.Remove(elem.Owner.Owner.Internals.CustomValueKey); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDate.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDate.cs new file mode 100644 index 00000000..ac4caefb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDate.cs @@ -0,0 +1,91 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a direct date value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfDate : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + public PdfDate() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfDate(string value) + { + _value = Parser.ParseDateTime(value, DateTime.MinValue); + } + + /// + /// Initializes a new instance of the class. + /// + public PdfDate(DateTime value) + { + _value = value; + } + + /// + /// Gets the value as DateTime. + /// + public DateTime Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + DateTime _value; + + /// + /// Returns the value in the PDF date format. + /// + public override string ToString() + { + string delta = _value.ToString("zzz").Replace(':', '\''); + return String.Format("D:{0:yyyyMMddHHmmss}{1}'", _value, delta); + } + + /// + /// Writes the value in the PDF date format. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteDocString(ToString()); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDictionary.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDictionary.cs new file mode 100644 index 00000000..428140a1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDictionary.cs @@ -0,0 +1,1912 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Text; +using PdfSharp.Drawing; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Filters; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Value creation flags. Specifies whether and how a value that does not exist is created. + /// + // ReSharper disable InconsistentNaming + public enum VCF + // ReSharper restore InconsistentNaming + { + /// + /// Don't create the value. + /// + None, + + /// + /// Create the value as direct object. + /// + Create, + + /// + /// Create the value as indirect object. + /// + CreateIndirect, + } + + /// + /// Represents a PDF dictionary object. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public class PdfDictionary : PdfObject, IEnumerable> + { + // Reference: 3.2.6  Dictionary Objects / Page 59 + + /// + /// Initializes a new instance of the class. + /// + public PdfDictionary() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfDictionary(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance from an existing dictionary. Used for object type transformation. + /// + protected PdfDictionary(PdfDictionary dict) + : base(dict) + { + if (dict._elements != null) + dict._elements.ChangeOwner(this); + if (dict._stream != null) + dict._stream.ChangeOwner(this); + } + + /// + /// Creates a copy of this dictionary. Direct values are deep copied. Indirect references are not + /// modified. + /// + public new PdfDictionary Clone() + { + return (PdfDictionary)Copy(); + } + + /// + /// This function is useful for importing objects from external documents. The returned object is not + /// yet complete. irefs refer to external objects and directed objects are cloned but their document + /// property is null. A cloned dictionary or array needs a 'fix-up' to be a valid object. + /// + protected override object Copy() + { + PdfDictionary dict = (PdfDictionary)base.Copy(); + if (dict._elements != null) + { + dict._elements = dict._elements.Clone(); + dict._elements.ChangeOwner(dict); + PdfName[] names = dict._elements.KeyNames; + foreach (PdfName name in names) + { + PdfObject obj = dict._elements[name] as PdfObject; + if (obj != null) + { + obj = obj.Clone(); + // Recall that obj.Document is now null. + dict._elements[name] = obj; + } + } + } + if (dict._stream != null) + { + dict._stream = dict._stream.Clone(); + dict._stream.ChangeOwner(dict); + } + return dict; + } + + /// + /// Gets the dictionary containing the elements of this dictionary. + /// + public DictionaryElements Elements + { + get { return _elements ?? (_elements = new DictionaryElements(this)); } + } + + /// + /// The elements of the dictionary. + /// + internal DictionaryElements _elements; + + /// + /// Returns an enumerator that iterates through the dictionary elements. + /// + public IEnumerator> GetEnumerator() + { + return Elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Returns a string with the content of this object in a readable form. Useful for debugging purposes only. + /// + public override string ToString() + { + // Get keys and sort. + PdfName[] keys = Elements.KeyNames; + List list = new List(keys); + list.Sort(PdfName.Comparer); + list.CopyTo(keys, 0); + + StringBuilder pdf = new StringBuilder(); + pdf.Append("<< "); + foreach (PdfName key in keys) + pdf.Append(key + " " + Elements[key] + " "); + pdf.Append(">>"); + + return pdf.ToString(); + } + + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + //int count = Elements.Count; + PdfName[] keys = Elements.KeyNames; + +#if DEBUG + // TODO: automatically set length + if (_stream != null) + Debug.Assert(Elements.ContainsKey(PdfStream.Keys.Length), "Dictionary has a stream but no length is set."); +#endif + +#if DEBUG + // Sort keys for debugging purposes. Comparing PDF files with for example programs like + // Araxis Merge is easier with sorted keys. + if (writer.Layout == PdfWriterLayout.Verbose) + { + List list = new List(keys); + list.Sort(PdfName.Comparer); + list.CopyTo(keys, 0); + } +#endif + + foreach (PdfName key in keys) + WriteDictionaryElement(writer, key); + if (Stream != null) + WriteDictionaryStream(writer); + writer.WriteEndObject(); + } + + /// + /// Writes a key/value pair of this dictionary. This function is intended to be overridden + /// in derived classes. + /// + internal virtual void WriteDictionaryElement(PdfWriter writer, PdfName key) + { + if (key == null) + throw new ArgumentNullException("key"); + PdfItem item = Elements[key]; +#if DEBUG + // TODO: simplify PDFsharp + if (item is PdfObject && ((PdfObject)item).IsIndirect) + { + // Replace an indirect object by its Reference. + item = ((PdfObject)item).Reference; + Debug.Assert(false, "Check when we come here."); + } +#endif + key.WriteObject(writer); + item.WriteObject(writer); + writer.NewLine(); + } + + /// + /// Writes the stream of this dictionary. This function is intended to be overridden + /// in a derived class. + /// + internal virtual void WriteDictionaryStream(PdfWriter writer) + { + writer.WriteStream(this, (writer.Options & PdfWriterOptions.OmitStream) == PdfWriterOptions.OmitStream); + } + + /// + /// Gets or sets the PDF stream belonging to this dictionary. Returns null if the dictionary has + /// no stream. To create the stream, call the CreateStream function. + /// + public PdfStream Stream + { + get { return _stream; } + set { _stream = value; } + } + PdfStream _stream; + + /// + /// Creates the stream of this dictionary and initializes it with the specified byte array. + /// The function must not be called if the dictionary already has a stream. + /// + public PdfStream CreateStream(byte[] value) + { + if (_stream != null) + throw new InvalidOperationException("The dictionary already has a stream."); + + _stream = new PdfStream(value, this); + // Always set the length. + Elements[PdfStream.Keys.Length] = new PdfInteger(_stream.Length); + return _stream; + } + + /// + /// When overridden in a derived class, gets the KeysMeta of this dictionary type. + /// + internal virtual DictionaryMeta Meta + { + get { return null; } + } + + /// + /// Represents the interface to the elements of a PDF dictionary. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public sealed class DictionaryElements : IDictionary, ICloneable + { + internal DictionaryElements(PdfDictionary ownerDictionary) + { + _elements = new Dictionary(); + _ownerDictionary = ownerDictionary; + } + + object ICloneable.Clone() + { + DictionaryElements dictionaryElements = (DictionaryElements)MemberwiseClone(); + dictionaryElements._elements = new Dictionary(dictionaryElements._elements); + dictionaryElements._ownerDictionary = null; + return dictionaryElements; + } + + /// + /// Creates a shallow copy of this object. The clone is not owned by a dictionary anymore. + /// + public DictionaryElements Clone() + { + return (DictionaryElements)((ICloneable)this).Clone(); + } + + /// + /// Moves this instance to another dictionary during object type transformation. + /// + internal void ChangeOwner(PdfDictionary ownerDictionary) + { + if (_ownerDictionary != null) + { + // ??? + } + + // Set new owner. + _ownerDictionary = ownerDictionary; + + // Set owners elements to this. + ownerDictionary._elements = this; + } + + /// + /// Gets the dictionary to which this elements object belongs to. + /// + internal PdfDictionary Owner + { + get { return _ownerDictionary; } + } + + /// + /// Converts the specified value to boolean. + /// If the value does not exist, the function returns false. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public bool GetBoolean(string key, bool create) + { + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfBoolean(); + return false; + } + + if (obj is PdfReference) + obj = ((PdfReference)obj).Value; + + PdfBoolean boolean = obj as PdfBoolean; + if (boolean != null) + return boolean.Value; + + PdfBooleanObject booleanObject = obj as PdfBooleanObject; + if (booleanObject != null) + return booleanObject.Value; + throw new InvalidCastException("GetBoolean: Object is not a boolean."); + } + + /// + /// Converts the specified value to boolean. + /// If the value does not exist, the function returns false. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public bool GetBoolean(string key) + { + return GetBoolean(key, false); + } + + /// + /// Sets the entry to a direct boolean value. + /// + public void SetBoolean(string key, bool value) + { + this[key] = new PdfBoolean(value); + } + + /// + /// Converts the specified value to integer. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public int GetInteger(string key, bool create) + { + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfInteger(); + return 0; + } + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfInteger integer = obj as PdfInteger; + if (integer != null) + return integer.Value; + + PdfIntegerObject integerObject = obj as PdfIntegerObject; + if (integerObject != null) + return integerObject.Value; + + throw new InvalidCastException("GetInteger: Object is not an integer."); + } + + /// + /// Converts the specified value to integer. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public int GetInteger(string key) + { + return GetInteger(key, false); + } + + /// + /// Sets the entry to a direct integer value. + /// + public void SetInteger(string key, int value) + { + this[key] = new PdfInteger(value); + } + + /// + /// Converts the specified value to double. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public double GetReal(string key, bool create) + { + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfReal(); + return 0; + } + + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfReal real = obj as PdfReal; + if (real != null) + return real.Value; + + PdfRealObject realObject = obj as PdfRealObject; + if (realObject != null) + return realObject.Value; + + PdfInteger integer = obj as PdfInteger; + if (integer != null) + return integer.Value; + + PdfIntegerObject integerObject = obj as PdfIntegerObject; + if (integerObject != null) + return integerObject.Value; + + throw new InvalidCastException("GetReal: Object is not a number."); + } + + /// + /// Converts the specified value to double. + /// If the value does not exist, the function returns 0. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public double GetReal(string key) + { + return GetReal(key, false); + } + + /// + /// Sets the entry to a direct double value. + /// + public void SetReal(string key, double value) + { + this[key] = new PdfReal(value); + } + + /// + /// Converts the specified value to String. + /// If the value does not exist, the function returns the empty string. + /// + public string GetString(string key, bool create) + { + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfString(); + return ""; + } + + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfString str = obj as PdfString; + if (str != null) + return str.Value; + + PdfStringObject strObject = obj as PdfStringObject; + if (strObject != null) + return strObject.Value; + + PdfName name = obj as PdfName; + if (name != null) + return name.Value; + + PdfNameObject nameObject = obj as PdfNameObject; + if (nameObject != null) + return nameObject.Value; + + throw new InvalidCastException("GetString: Object is not a string."); + } + + /// + /// Converts the specified value to String. + /// If the value does not exist, the function returns the empty string. + /// + public string GetString(string key) + { + return GetString(key, false); + } + + /// + /// Tries to get the string. TODO: more TryGet... + /// + public bool TryGetString(string key, out string value) + { + value = null; + object obj = this[key]; + if (obj == null) + return false; + + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfString str = obj as PdfString; + if (str != null) + { + value = str.Value; + return true; + } + + PdfStringObject strObject = obj as PdfStringObject; + if (strObject != null) + { + value = strObject.Value; + return true; + } + + PdfName name = obj as PdfName; + if (name != null) + { + value = name.Value; + return true; + } + + PdfNameObject nameObject = obj as PdfNameObject; + if (nameObject != null) + { + value = nameObject.Value; + return true; + } + + return false; + } + + /// + /// Sets the entry to a direct string value. + /// + public void SetString(string key, string value) + { + this[key] = new PdfString(value); + } + + /// + /// Converts the specified value to a name. + /// If the value does not exist, the function returns the empty string. + /// + public string GetName(string key) + { + object obj = this[key]; + if (obj == null) + { + //if (create) + // this[key] = new Pdf(); + return String.Empty; + } + + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfName name = obj as PdfName; + if (name != null) + return name.Value; + + PdfNameObject nameObject = obj as PdfNameObject; + if (nameObject != null) + return nameObject.Value; + + throw new InvalidCastException("GetName: Object is not a name."); + } + + /// + /// Sets the specified name value. + /// If the value doesn't start with a slash, it is added automatically. + /// + public void SetName(string key, string value) + { + if (value == null) + throw new ArgumentNullException("value"); + + if (value.Length == 0 || value[0] != '/') + value = "/" + value; + + this[key] = new PdfName(value); + } + + /// + /// Converts the specified value to PdfRectangle. + /// If the value does not exist, the function returns an empty rectangle. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public PdfRectangle GetRectangle(string key, bool create) + { + PdfRectangle value = new PdfRectangle(); + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = value = new PdfRectangle(); + return value; + } + if (obj is PdfReference) + obj = ((PdfReference)obj).Value; + + PdfArray array = obj as PdfArray; + if (array != null && array.Elements.Count == 4) + { + value = new PdfRectangle(array.Elements.GetReal(0), array.Elements.GetReal(1), + array.Elements.GetReal(2), array.Elements.GetReal(3)); + this[key] = value; + } + else + value = (PdfRectangle)obj; + return value; + } + + /// + /// Converts the specified value to PdfRectangle. + /// If the value does not exist, the function returns an empty rectangle. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public PdfRectangle GetRectangle(string key) + { + return GetRectangle(key, false); + } + + /// + /// Sets the entry to a direct rectangle value, represented by an array with four values. + /// + public void SetRectangle(string key, PdfRectangle rect) + { + _elements[key] = rect; + } + + /// Converts the specified value to XMatrix. + /// If the value does not exist, the function returns an identity matrix. + /// If the value is not convertible, the function throws an InvalidCastException. + public XMatrix GetMatrix(string key, bool create) + { + XMatrix value = new XMatrix(); + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfLiteral("[1 0 0 1 0 0]"); // cannot be parsed, implement a PdfMatrix... + return value; + } + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfArray array = obj as PdfArray; + if (array != null && array.Elements.Count == 6) + { + value = new XMatrix(array.Elements.GetReal(0), array.Elements.GetReal(1), array.Elements.GetReal(2), + array.Elements.GetReal(3), array.Elements.GetReal(4), array.Elements.GetReal(5)); + } + else if (obj is PdfLiteral) + { + throw new NotImplementedException("Parsing matrix from literal."); + } + else + throw new InvalidCastException("Element is not an array with 6 values."); + return value; + } + + /// Converts the specified value to XMatrix. + /// If the value does not exist, the function returns an identity matrix. + /// If the value is not convertible, the function throws an InvalidCastException. + public XMatrix GetMatrix(string key) + { + return GetMatrix(key, false); + } + + /// + /// Sets the entry to a direct matrix value, represented by an array with six values. + /// + public void SetMatrix(string key, XMatrix matrix) + { + _elements[key] = PdfLiteral.FromMatrix(matrix); + } + + /// + /// Converts the specified value to DateTime. + /// If the value does not exist, the function returns the specified default value. + /// If the value is not convertible, the function throws an InvalidCastException. + /// + public DateTime GetDateTime(string key, DateTime defaultValue) + { + object obj = this[key]; + if (obj == null) + { + return defaultValue; + } + + PdfReference reference = obj as PdfReference; + if (reference != null) + obj = reference.Value; + + PdfDate date = obj as PdfDate; + if (date != null) + return date.Value; + + string strDate; + PdfString pdfString = obj as PdfString; + if (pdfString != null) + strDate = pdfString.Value; + else + { + PdfStringObject stringObject = obj as PdfStringObject; + if (stringObject != null) + strDate = stringObject.Value; + else + throw new InvalidCastException("GetName: Object is not a name."); + } + + if (strDate != "") + { + try + { + defaultValue = Parser.ParseDateTime(strDate, defaultValue); + } + // ReSharper disable EmptyGeneralCatchClause + catch { } + // ReSharper restore EmptyGeneralCatchClause + } + return defaultValue; + } + + /// + /// Sets the entry to a direct datetime value. + /// + public void SetDateTime(string key, DateTime value) + { + _elements[key] = new PdfDate(value); + } + + internal int GetEnumFromName(string key, object defaultValue, bool create) + { + if (!(defaultValue is Enum)) + throw new ArgumentException("defaultValue"); + + object obj = this[key]; + if (obj == null) + { + if (create) + this[key] = new PdfName(defaultValue.ToString()); + + // ReSharper disable once PossibleInvalidCastException because Enum objects can always be casted to int. + return (int)defaultValue; + } + Debug.Assert(obj is Enum); + return (int)Enum.Parse(defaultValue.GetType(), obj.ToString().Substring(1), false); + } + + internal int GetEnumFromName(string key, object defaultValue) + { + return GetEnumFromName(key, defaultValue, false); + } + + internal void SetEnumAsName(string key, object value) + { + if (!(value is Enum)) + throw new ArgumentException("value"); + _elements[key] = new PdfName("/" + value); + } + + /// + /// Gets the value for the specified key. If the value does not exist, it is optionally created. + /// + public PdfItem GetValue(string key, VCF options) + { + PdfObject obj; + PdfDictionary dict; + PdfArray array; + PdfReference iref; + PdfItem value = this[key]; + if (value == null || + value is PdfNull || + value is PdfReference && ((PdfReference)value).Value is PdfNullObject) + { + if (options != VCF.None) + { +#if NETFX_CORE && DEBUG_ + if (key == "/Resources") + Debug-Break.Break(); +#endif + Type type = GetValueType(key); + if (type != null) + { +#if !NETFX_CORE + Debug.Assert(typeof(PdfItem).IsAssignableFrom(type), "Type not allowed."); + if (typeof(PdfDictionary).IsAssignableFrom(type)) + { + value = obj = CreateDictionary(type, null); + } + else if (typeof(PdfArray).IsAssignableFrom(type)) + { + value = obj = CreateArray(type, null); + } + else + throw new NotImplementedException("Type other than array or dictionary."); +#else + // Rewritten WinRT style. + TypeInfo typeInfo = type.GetTypeInfo(); + Debug.Assert(typeof(PdfItem).GetTypeInfo().IsAssignableFrom(typeInfo), "Type not allowed."); + if (typeof(PdfDictionary).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + value = obj = CreateDictionary(type, null); + } + else if (typeof(PdfArray).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + value = obj = CreateArray(type, null); + } + else + throw new NotImplementedException("Type other than array or dictionary."); +#endif + if (options == VCF.CreateIndirect) + { + _ownerDictionary.Owner._irefTable.Add(obj); + this[key] = obj.Reference; + } + else + this[key] = obj; + } + else + throw new NotImplementedException("Cannot create value for key: " + key); + } + } + else + { + // The value exists and can be returned. But for imported documents check for necessary + // object type transformation. + if ((iref = value as PdfReference) != null) + { + // Case: value is an indirect reference. + value = iref.Value; + if (value == null) + { + // If we come here PDF file is corrupted. + throw new InvalidOperationException("Indirect reference without value."); + } + + if (true) // || _owner.Document.IsImported) + { + Type type = GetValueType(key); + Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support."); + +#if !NETFX_CORE + if (type != null && type != value.GetType()) + { + if (typeof(PdfDictionary).IsAssignableFrom(type)) + { + Debug.Assert(value is PdfDictionary, "Bug in PDFsharp. Please send this file to PDFsharp support."); + value = CreateDictionary(type, (PdfDictionary)value); + } + else if (typeof(PdfArray).IsAssignableFrom(type)) + { + Debug.Assert(value is PdfArray, "Bug in PDFsharp. Please send this file to PDFsharp support."); + value = CreateArray(type, (PdfArray)value); + } + else + throw new NotImplementedException("Type other than array or dictionary."); + } +#else + // Rewritten WinRT style. + TypeInfo typeInfo = type.GetTypeInfo(); + if (type != null && type != value.GetType()) + { + if (typeof(PdfDictionary).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + Debug.Assert(value is PdfDictionary, "Bug in PDFsharp. Please send this file to PDFsharp support."); + value = CreateDictionary(type, (PdfDictionary)value); + } + else if (typeof(PdfArray).GetTypeInfo().IsAssignableFrom(typeInfo)) + { + Debug.Assert(value is PdfArray, "Bug in PDFsharp. Please send this file to PDFsharp support."); + value = CreateArray(type, (PdfArray)value); + } + else + throw new NotImplementedException("Type other than array or dictionary."); + } +#endif + } + return value; + } + + // Transformation is only possible after PDF import. + if (true) // || _owner.Document.IsImported) + { + // Case: value is a direct object + if ((dict = value as PdfDictionary) != null) + { + Debug.Assert(!dict.IsIndirect); + + Type type = GetValueType(key); + Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support."); + if (dict.GetType() != type) + dict = CreateDictionary(type, dict); + return dict; + } + + if ((array = value as PdfArray) != null) + { + Debug.Assert(!array.IsIndirect); + + Type type = GetValueType(key); + // This is more complicated. If type is null do nothing + //Debug.Assert(type != null, "No value type specified in meta information. Please send this file to PDFsharp support."); + if (type != null && type != array.GetType()) + array = CreateArray(type, array); + return array; + } + } + } + return value; + } + + /// + /// Short cut for GetValue(key, VCF.None). + /// + public PdfItem GetValue(string key) + { + return GetValue(key, VCF.None); + } + + /// + /// Returns the type of the object to be created as value of the specified key. + /// + Type GetValueType(string key) // TODO: move to PdfObject + { + Type type = null; + DictionaryMeta meta = _ownerDictionary.Meta; + if (meta != null) + { + KeyDescriptor kd = meta[key]; + if (kd != null) + type = kd.GetValueType(); + //else + // Debug.WriteLine("Warning: Key not descriptor table: " + key); // TODO: check what this means... + } + //else + // Debug.WriteLine("Warning: No meta provided for type: " + _owner.GetType().Name); // TODO: check what this means... + return type; + } + + PdfArray CreateArray(Type type, PdfArray oldArray) + { +#if !NETFX_CORE && !UWP + ConstructorInfo ctorInfo; + PdfArray array; + if (oldArray == null) + { + // Use constructor with signature 'Ctor(PdfDocument owner)'. + ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, new Type[] { typeof(PdfDocument) }, null); + Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name); + array = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfArray; + } + else + { + // Use constructor with signature 'Ctor(PdfDictionary dict)'. + ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, new Type[] { typeof(PdfArray) }, null); + Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name); + array = ctorInfo.Invoke(new object[] { oldArray }) as PdfArray; + } + return array; +#else + // Rewritten WinRT style. + PdfArray array = null; + if (oldArray == null) + { + // Use constructor with signature 'Ctor(PdfDocument owner)'. + var ctorInfos = type.GetTypeInfo().DeclaredConstructors; //.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + //null, new Type[] { typeof(PdfDocument) }, null); + foreach (var ctorInfo in ctorInfos) + { + var parameters = ctorInfo.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(PdfDocument)) + { + array = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfArray; + break; + } + } + Debug.Assert(array != null, "No appropriate constructor found for type: " + type.Name); + } + else + { + // Use constructor with signature 'Ctor(PdfDictionary dict)'. + var ctorInfos = type.GetTypeInfo().DeclaredConstructors; // .GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + //null, new Type[] { typeof(PdfArray) }, null); + foreach (var ctorInfo in ctorInfos) + { + var parameters = ctorInfo.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(PdfArray)) + { + array = ctorInfo.Invoke(new object[] { oldArray }) as PdfArray; + break; + } + } + Debug.Assert(array != null, "No appropriate constructor found for type: " + type.Name); + } + return array; +#endif + } + + PdfDictionary CreateDictionary(Type type, PdfDictionary oldDictionary) + { +#if !NETFX_CORE && !UWP + ConstructorInfo ctorInfo; + PdfDictionary dict; + if (oldDictionary == null) + { + // Use constructor with signature 'Ctor(PdfDocument owner)'. + ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, new Type[] { typeof(PdfDocument) }, null); + Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name); + dict = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfDictionary; + } + else + { + // Use constructor with signature 'Ctor(PdfDictionary dict)'. + ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, new Type[] { typeof(PdfDictionary) }, null); + Debug.Assert(ctorInfo != null, "No appropriate constructor found for type: " + type.Name); + dict = ctorInfo.Invoke(new object[] { oldDictionary }) as PdfDictionary; + } + return dict; +#else + // Rewritten WinRT style. + PdfDictionary dict = null; + if (oldDictionary == null) + { + // Use constructor with signature 'Ctor(PdfDocument owner)'. + var ctorInfos = type.GetTypeInfo().DeclaredConstructors; //GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + //null, new Type[] { typeof(PdfDocument) }, null); + foreach (var ctorInfo in ctorInfos) + { + var parameters = ctorInfo.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(PdfDocument)) + { + dict = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfDictionary; + break; + } + } + Debug.Assert(dict != null, "No appropriate constructor found for type: " + type.Name); + } + else + { + var ctorInfos = type.GetTypeInfo().DeclaredConstructors; // GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(PdfDictionary) }, null); + foreach (var ctorInfo in ctorInfos) + { + var parameters = ctorInfo.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(PdfDictionary)) + { + dict = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfDictionary; + break; + } + } + Debug.Assert(dict != null, "No appropriate constructor found for type: " + type.Name); + } + return dict; +#endif + } + + PdfItem CreateValue(Type type, PdfDictionary oldValue) + { +#if !NETFX_CORE && !UWP + ConstructorInfo ctorInfo = type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, new Type[] { typeof(PdfDocument) }, null); + PdfObject obj = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfObject; + if (oldValue != null) + { + obj.Reference = oldValue.Reference; + obj.Reference.Value = obj; + if (obj is PdfDictionary) + { + PdfDictionary dict = (PdfDictionary)obj; + dict._elements = oldValue._elements; + } + } + return obj; +#else + // Rewritten WinRT style. + PdfObject obj = null; + var ctorInfos = type.GetTypeInfo().DeclaredConstructors; // GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(PdfDocument) }, null); + foreach (var ctorInfo in ctorInfos) + { + var parameters = ctorInfo.GetParameters(); + if (parameters.Length == 1 && parameters[0].ParameterType == typeof(PdfDocument)) + { + obj = ctorInfo.Invoke(new object[] { _ownerDictionary.Owner }) as PdfObject; + break; + } + } + Debug.Assert(obj != null, "No appropriate constructor found for type: " + type.Name); + if (oldValue != null) + { + obj.Reference = oldValue.Reference; + obj.Reference.Value = obj; + if (obj is PdfDictionary) + { + PdfDictionary dict = (PdfDictionary)obj; + dict._elements = oldValue._elements; + } + } + return obj; +#endif + } + + /// + /// Sets the entry with the specified value. DON'T USE THIS FUNCTION - IT MAY BE REMOVED. + /// + public void SetValue(string key, PdfItem value) + { + Debug.Assert((value is PdfObject && ((PdfObject)value).Reference == null) | !(value is PdfObject), + "You try to set an indirect object directly into a dictionary."); + + // HACK? + _elements[key] = value; + } + + ///// + ///// Returns the indirect object if the value of the specified key is a PdfReference. + ///// + //[Obsolete("Use GetObject, GetDictionary, GetArray, or GetReference")] + //public PdfObject GetIndirectObject(string key) + //{ + // PdfItem item = this[key]; + // if (item is PdfReference) + // return ((PdfReference)item).Value; + // return null; + //} + + /// + /// Gets the PdfObject with the specified key, or null, if no such object exists. If the key refers to + /// a reference, the referenced PdfObject is returned. + /// + public PdfObject GetObject(string key) + { + PdfItem item = this[key]; + PdfReference reference = item as PdfReference; + if (reference != null) + return reference.Value; + return item as PdfObject; + } + + /// + /// Gets the PdfDictionary with the specified key, or null, if no such object exists. If the key refers to + /// a reference, the referenced PdfDictionary is returned. + /// + public PdfDictionary GetDictionary(string key) + { + return GetObject(key) as PdfDictionary; + } + + /// + /// Gets the PdfArray with the specified key, or null, if no such object exists. If the key refers to + /// a reference, the referenced PdfArray is returned. + /// + public PdfArray GetArray(string key) + { + return GetObject(key) as PdfArray; + } + + /// + /// Gets the PdfReference with the specified key, or null, if no such object exists. + /// + public PdfReference GetReference(string key) + { + PdfItem item = this[key]; + return item as PdfReference; + } + + /// + /// Sets the entry to the specified object. The object must not be an indirect object, + /// otherwise an exception is raised. + /// + public void SetObject(string key, PdfObject obj) + { + if (obj.Reference != null) + throw new ArgumentException("PdfObject must not be an indirect object.", "obj"); + this[key] = obj; + } + + /// + /// Sets the entry as a reference to the specified object. The object must be an indirect object, + /// otherwise an exception is raised. + /// + public void SetReference(string key, PdfObject obj) + { + if (obj.Reference == null) + throw new ArgumentException("PdfObject must be an indirect object.", "obj"); + this[key] = obj.Reference; + } + + /// + /// Sets the entry as a reference to the specified iref. + /// + public void SetReference(string key, PdfReference iref) + { + if (iref == null) + throw new ArgumentNullException("iref"); + this[key] = iref; + } + + #region IDictionary Members + + /// + /// Gets a value indicating whether the object is read-only. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Returns an object for the object. + /// + public IEnumerator> GetEnumerator() + { + return _elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((ICollection)_elements).GetEnumerator(); + } + + /// + /// Gets or sets an entry in the dictionary. The specified key must be a valid PDF name + /// starting with a slash '/'. This property provides full access to the elements of the + /// PDF dictionary. Wrong use can lead to errors or corrupt PDF files. + /// + public PdfItem this[string key] + { + get + { + PdfItem item; + _elements.TryGetValue(key, out item); + return item; + } + set + { + if (value == null) + throw new ArgumentNullException("value"); +#if DEBUG_ + if (key == "/MediaBox") + key.GetType(); + + //if (value is PdfObject) + //{ + // PdfObject obj = (PdfObject)value; + // if (obj.Reference != null) + // throw new ArgumentException("An object with an indirect reference cannot be a direct value. Try to set an indirect reference."); + //} + if (value is PdfDictionary) + { + PdfDictionary dict = (PdfDictionary)value; + if (dict._stream != null) + throw new ArgumentException("A dictionary with stream cannot be a direct value."); + } +#endif + PdfObject obj = value as PdfObject; + if (obj != null && obj.IsIndirect) + value = obj.Reference; + _elements[key] = value; + } + } + + /// + /// Gets or sets an entry in the dictionary identified by a PdfName object. + /// + public PdfItem this[PdfName key] + { + get { return this[key.Value]; } + set + { + if (value == null) + throw new ArgumentNullException("value"); + +#if DEBUG + PdfDictionary dictionary = value as PdfDictionary; + if (dictionary != null) + { + PdfDictionary dict = dictionary; + if (dict._stream != null) + throw new ArgumentException("A dictionary with stream cannot be a direct value."); + } +#endif + + PdfObject obj = value as PdfObject; + if (obj != null && obj.IsIndirect) + value = obj.Reference; + _elements[key.Value] = value; + } + } + + /// + /// Removes the value with the specified key. + /// + public bool Remove(string key) + { + return _elements.Remove(key); + } + + /// + /// Removes the value with the specified key. + /// + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + ///// + ///// Determines whether the dictionary contains the specified name. + ///// + //[Obsolete("Use ContainsKey.")] + //public bool Contains(string key) + //{ + // return _elements.ContainsKey(key); + //} + + /// + /// Determines whether the dictionary contains the specified name. + /// + public bool ContainsKey(string key) + { + return _elements.ContainsKey(key); + } + + /// + /// Determines whether the dictionary contains a specific value. + /// + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + /// + /// Removes all elements from the dictionary. + /// + public void Clear() + { + _elements.Clear(); + } + + /// + /// Adds the specified value to the dictionary. + /// + public void Add(string key, PdfItem value) + { + if (String.IsNullOrEmpty(key)) + throw new ArgumentNullException("key"); + + if (key[0] != '/') + throw new ArgumentException("The key must start with a slash '/'."); + + // If object is indirect automatically convert value to reference. + PdfObject obj = value as PdfObject; + if (obj != null && obj.IsIndirect) + value = obj.Reference; + + _elements.Add(key, value); + } + + /// + /// Adds an item to the dictionary. + /// + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + /// + /// Gets all keys currently in use in this dictionary as an array of PdfName objects. + /// + public PdfName[] KeyNames + { + get + { + ICollection values = _elements.Keys; + int count = values.Count; + string[] strings = new string[count]; + values.CopyTo(strings, 0); + PdfName[] names = new PdfName[count]; + for (int idx = 0; idx < count; idx++) + names[idx] = new PdfName(strings[idx]); + return names; + } + } + + /// + /// Get all keys currently in use in this dictionary as an array of string objects. + /// + public ICollection Keys + { + // It is by design not to return _elements.Keys, but a copy. + get + { + ICollection values = _elements.Keys; + int count = values.Count; + string[] keys = new string[count]; + values.CopyTo(keys, 0); + return keys; + } + } + + /// + /// Gets the value associated with the specified key. + /// + public bool TryGetValue(string key, out PdfItem value) + { + return _elements.TryGetValue(key, out value); + } + + /// + /// Gets all values currently in use in this dictionary as an array of PdfItem objects. + /// + //public ICollection Values + public ICollection Values + { + // It is by design not to return _elements.Values, but a copy. + get + { + ICollection values = _elements.Values; + PdfItem[] items = new PdfItem[values.Count]; + values.CopyTo(items, 0); + return items; + } + } + + /// + /// Return false. + /// + public bool IsFixedSize + { + get { return false; } + } + + #endregion + + #region ICollection Members + + /// + /// Return false. + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + /// Gets the number of elements contained in the dictionary. + /// + public int Count + { + get { return _elements.Count; } + } + + /// + /// Copies the elements of the dictionary to an array, starting at a particular index. + /// + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + /// + /// The current implementation returns null. + /// + public object SyncRoot + { + get { return null; } + } + + #endregion + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + internal string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat(CultureInfo.InvariantCulture, "key={0}:(", _elements.Count); + bool addSpace = false; + ICollection keys = _elements.Keys; + foreach (string key in keys) + { + if (addSpace) + sb.Append(' '); + addSpace = true; + sb.Append(key); + } + sb.Append(")"); + return sb.ToString(); + } + } + + /// + /// The elements of the dictionary with a string as key. + /// Because the string is a name it starts always with a '/'. + /// + Dictionary _elements; + + /// + /// The dictionary this objects belongs to. + /// + PdfDictionary _ownerDictionary; + } + + /// + /// The PDF stream objects. + /// + public sealed class PdfStream + { + internal PdfStream(PdfDictionary ownerDictionary) + { + if (ownerDictionary == null) + throw new ArgumentNullException("ownerDictionary"); + _ownerDictionary = ownerDictionary; + } + + /// + /// A .NET string can contain char(0) as a valid character. + /// + internal PdfStream(byte[] value, PdfDictionary owner) + : this(owner) + { + _value = value; + } + + /// + /// Clones this stream by creating a deep copy. + /// + public PdfStream Clone() + { + PdfStream stream = (PdfStream)MemberwiseClone(); + stream._ownerDictionary = null; + if (stream._value != null) + { + stream._value = new byte[stream._value.Length]; + _value.CopyTo(stream._value, 0); + } + return stream; + } + + /// + /// Moves this instance to another dictionary during object type transformation. + /// + internal void ChangeOwner(PdfDictionary dict) + { + if (_ownerDictionary != null) + { + // ??? + } + + // Set new owner. + _ownerDictionary = dict; + + // Set owners stream to this. + _ownerDictionary._stream = this; + } + + /// + /// The dictionary the stream belongs to. + /// + PdfDictionary _ownerDictionary; + + /// + /// Gets the length of the stream, i.e. the actual number of bytes in the stream. + /// + public int Length + { + get { return _value != null ? _value.Length : 0; } + } + + /// + /// Gets a value indicating whether this stream has decode parameters. + /// + internal bool HasDecodeParams + { + // TODO: Move to Stream.Internals + get + { + // TODO: DecodeParams can be an array. + PdfDictionary dictionary = _ownerDictionary.Elements.GetDictionary(Keys.DecodeParms); + if (dictionary != null) + { + // More to do here? + return true; + } + return false; + } + } + + /// + /// Gets the decode predictor for LZW- or FlateDecode. + /// Returns 0 if no such value exists. + /// + internal int DecodePredictor // Reference: TABLE 3.8 Predictor values / Page 76 + { + get + { + PdfDictionary dictionary = _ownerDictionary.Elements.GetDictionary(Keys.DecodeParms); + if (dictionary != null) + { + return dictionary.Elements.GetInteger("/Predictor"); + } + return 0; + } + } + + /// + /// Gets the decode Columns for LZW- or FlateDecode. + /// Returns 0 if no such value exists. + /// + internal int DecodeColumns // Reference: TABLE 3.8 Predictor values / Page 76 + { + get + { + PdfDictionary dictionary = _ownerDictionary.Elements.GetDictionary(Keys.DecodeParms); + if (dictionary != null) + { + return dictionary.Elements.GetInteger("/Columns"); + } + return 0; + } + } + + /// + /// Get or sets the bytes of the stream as they are, i.e. if one or more filters exist the bytes are + /// not unfiltered. + /// + public byte[] Value + { + get { return _value; } + set + { + if (value == null) + throw new ArgumentNullException("value"); + _value = value; + _ownerDictionary.Elements.SetInteger(Keys.Length, value.Length); + } + } + byte[] _value; + + /// + /// Gets the value of the stream unfiltered. The stream content is not modified by this operation. + /// + public byte[] UnfilteredValue + { + get + { + byte[] bytes = null; + if (_value != null) + { + PdfItem filter = _ownerDictionary.Elements["/Filter"]; + if (filter != null) + { + bytes = Filtering.Decode(_value, filter); + if (bytes == null) + { + string message = String.Format("«Cannot decode filter '{0}'»", filter); + bytes = PdfEncoders.RawEncoding.GetBytes(message); + } + } + else + { + bytes = new byte[_value.Length]; + _value.CopyTo(bytes, 0); + } + } + return bytes ?? new byte[0]; + } + } + + /// + /// Tries to unfilter the bytes of the stream. If the stream is filtered and PDFsharp knows the filter + /// algorithm, the stream content is replaced by its unfiltered value and the function returns true. + /// Otherwise the content remains untouched and the function returns false. + /// The function is useful for analyzing existing PDF files. + /// + public bool TryUnfilter() // TODO: Take DecodeParams into account. + { + if (_value != null) + { + PdfItem filter = _ownerDictionary.Elements["/Filter"]; + if (filter != null) + { + // PDFsharp can only uncompress streams that are compressed with the ZIP or LZH algorithm. + byte[] bytes = Filtering.Decode(_value, filter); + if (bytes != null) + { + _ownerDictionary.Elements.Remove(Keys.Filter); + Value = bytes; + } + else + return false; + } + } + return true; + } + + /// + /// Compresses the stream with the FlateDecode filter. + /// If a filter is already defined, the function has no effect. + /// + public void Zip() + { + if (_value == null) + return; + + if (!_ownerDictionary.Elements.ContainsKey("/Filter")) + { + _value = Filtering.FlateDecode.Encode(_value, _ownerDictionary._document.Options.FlateEncodeMode); + _ownerDictionary.Elements["/Filter"] = new PdfName("/FlateDecode"); + _ownerDictionary.Elements["/Length"] = new PdfInteger(_value.Length); + } + } + + /// + /// Returns the stream content as a raw string. + /// + public override string ToString() + { + if (_value == null) + return "«null»"; + + string stream; + PdfItem filter = _ownerDictionary.Elements["/Filter"]; + if (filter != null) + { +#if true + byte[] bytes = Filtering.Decode(_value, filter); + if (bytes != null) + stream = PdfEncoders.RawEncoding.GetString(bytes, 0, bytes.Length); +#else + + if (_owner.Elements.GetString("/Filter") == "/FlateDecode") + { + stream = Filtering.FlateDecode.DecodeToString(_value); + } +#endif + else + throw new NotImplementedException("Unknown filter"); + } + else + stream = PdfEncoders.RawEncoding.GetString(_value, 0, _value.Length); + + return stream; + } + + //internal void WriteObject_(Stream stream) + //{ + // if (_value != null) + // stream.Write(_value, 0, value.Length); + //} + + ///// + ///// Converts a raw encoded string into a byte array. + ///// + //public static byte[] RawEncode(string content) + //{ + // return PdfEncoders.RawEncoding.GetBytes(content); + //} + + /// + /// Common keys for all streams. + /// + public class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Required) The number of bytes from the beginning of the line following the keyword + /// stream to the last byte just before the keyword endstream. (There may be an additional + /// EOL marker, preceding endstream, that is not included in the count and is not logically + /// part of the stream data.) + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Length = "/Length"; + + /// + /// (Optional) The name of a filter to be applied in processing the stream data found between + /// the keywords stream and endstream, or an array of such names. Multiple filters should be + /// specified in the order in which they are to be applied. + /// + [KeyInfo(KeyType.NameOrArray | KeyType.Optional)] + public const string Filter = "/Filter"; + + /// + /// (Optional) A parameter dictionary or an array of such dictionaries, used by the filters + /// specified by Filter. If there is only one filter and that filter has parameters, DecodeParms + /// must be set to the filter’s parameter dictionary unless all the filter’s parameters have + /// their default values, in which case the DecodeParms entry may be omitted. If there are + /// multiple filters and any of the filters has parameters set to nondefault values, DecodeParms + /// must be an array with one entry for each filter: either the parameter dictionary for that + /// filter, or the null object if that filter has no parameters (or if all of its parameters have + /// their default values). If none of the filters have parameters, or if all their parameters + /// have default values, the DecodeParms entry may be omitted. + /// + [KeyInfo(KeyType.ArrayOrDictionary | KeyType.Optional)] + public const string DecodeParms = "/DecodeParms"; + + /// + /// (Optional; PDF 1.2) The file containing the stream data. If this entry is present, the bytes + /// between stream and endstream are ignored, the filters are specified by FFilter rather than + /// Filter, and the filter parameters are specified by FDecodeParms rather than DecodeParms. + /// However, the Length entry should still specify the number of those bytes. (Usually, there are + /// no bytes and Length is 0.) + /// + [KeyInfo("1.2", KeyType.String | KeyType.Optional)] + public const string F = "/F"; + + /// + /// (Optional; PDF 1.2) The name of a filter to be applied in processing the data found in the + /// stream’s external file, or an array of such names. The same rules apply as for Filter. + /// + [KeyInfo("1.2", KeyType.NameOrArray | KeyType.Optional)] + public const string FFilter = "/FFilter"; + + /// + /// (Optional; PDF 1.2) A parameter dictionary, or an array of such dictionaries, used by the + /// filters specified by FFilter. The same rules apply as for DecodeParms. + /// + [KeyInfo("1.2", KeyType.ArrayOrDictionary | KeyType.Optional)] + public const string FDecodeParms = "/FDecodeParms"; + + /// + /// Optional; PDF 1.5) A non-negative integer representing the number of bytes in the decoded + /// (defiltered) stream. It can be used to determine, for example, whether enough disk space is + /// available to write a stream to a file. + /// This value should be considered a hint only; for some stream filters, it may not be possible + /// to determine this value precisely. + /// + [KeyInfo("1.5", KeyType.Integer | KeyType.Optional)] + public const string DL = "/DL"; + + // ReSharper restore InconsistentNaming + } + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { +#if true + return String.Format(CultureInfo.InvariantCulture, "dictionary({0},[{1}])={2}", + ObjectID.DebuggerDisplay, + Elements.Count, + _elements.DebuggerDisplay); +#else + return String.Format(CultureInfo.InvariantCulture, "dictionary({0},[{1}])=", ObjectID.DebuggerDisplay, _elements.DebuggerDisplay); +#endif + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDocument.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocument.cs new file mode 100644 index 00000000..fe1b9af5 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocument.cs @@ -0,0 +1,1007 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.IO; +using PdfSharp.Events; +#if NETFX_CORE +using System.Threading.Tasks; +#endif +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Internal; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.AcroForms; +using PdfSharp.Pdf.Security; + +// ReSharper disable ConvertPropertyToExpressionBody + +namespace PdfSharp.Pdf +{ + /// + /// Represents a PDF document. + /// + [DebuggerDisplay("(Name={Name})")] // A name makes debugging easier + public sealed class PdfDocument : PdfObject, IDisposable + { + internal DocumentState _state; + internal PdfDocumentOpenMode _openMode; + +#if DEBUG_ + static PdfDocument() + { + PSSR.TestResourceMessages(); + //string test = PSSR.ResMngr.GetString("SampleMessage1"); + //test.GetType(); + } +#endif + + /// + /// Creates a new PDF document in memory. + /// To open an existing PDF file, use the PdfReader class. + /// + public PdfDocument() + { + //PdfDocument.Gob.AttatchDocument(Handle); + + _creation = DateTime.Now; + _state = DocumentState.Created; + _version = 14; + Initialize(); + Info.CreationDate = _creation; + } + + /// + /// Creates a new PDF document with the specified file name. The file is immediately created and keeps + /// locked until the document is closed, at that time the document is saved automatically. + /// Do not call Save() for documents created with this constructor, just call Close(). + /// To open an existing PDF file and import it, use the PdfReader class. + /// + public PdfDocument(string filename) + { + //PdfDocument.Gob.AttatchDocument(Handle); + + _creation = DateTime.Now; + _state = DocumentState.Created; + _version = 14; + Initialize(); + Info.CreationDate = _creation; + + // TODO 4STLA: encapsulate the whole c'tor with #if !NETFX_CORE? +#if !NETFX_CORE + _outStream = new FileStream(filename, FileMode.Create); +#else + throw new NotImplementedException(); +#endif + } + + /// + /// Creates a new PDF document using the specified stream. + /// The stream won't be used until the document is closed, at that time the document is saved automatically. + /// Do not call Save() for documents created with this constructor, just call Close(). + /// To open an existing PDF file, use the PdfReader class. + /// + public PdfDocument(Stream outputStream) + { + //PdfDocument.Gob.AttatchDocument(Handle); + + _creation = DateTime.Now; + _state = DocumentState.Created; + Initialize(); + Info.CreationDate = _creation; + + _outStream = outputStream; + } + + internal PdfDocument(Lexer lexer) + { + //PdfDocument.Gob.AttatchDocument(Handle); + + _creation = DateTime.Now; + _state = DocumentState.Imported; + + //_info = new PdfInfo(this); + //_pages = new PdfPages(this); + //_fontTable = new PdfFontTable(); + //_catalog = new PdfCatalog(this); + ////_font = new PdfFont(); + //_objects = new PdfObjectTable(this); + //_trailer = new PdfTrailer(this); + _irefTable = new PdfCrossReferenceTable(this); + _lexer = lexer; + } + + void Initialize() + { + //_info = new PdfInfo(this); + _fontTable = new PdfFontTable(this); + _imageTable = new PdfImageTable(this); + _trailer = new PdfTrailer(this); + _irefTable = new PdfCrossReferenceTable(this); + _trailer.CreateNewDocumentIDs(); + } + + //~PdfDocument() + //{ + // Dispose(false); + //} + + /// + /// Disposes all references to this document stored in other documents. This function should be called + /// for documents you finished importing pages from. Calling Dispose is technically not necessary but + /// useful for earlier reclaiming memory of documents you do not need anymore. + /// + public void Dispose() + { + Dispose(true); + //GC.SuppressFinalize(this); + } + + void Dispose(bool disposing) + { + if (_state != DocumentState.Disposed) + { + if (disposing) + { + // Dispose managed resources. + } + //PdfDocument.Gob.DetatchDocument(Handle); + } + _state = DocumentState.Disposed; + } + + /// + /// Gets or sets a user defined object that contains arbitrary information associated with this document. + /// The tag is not used by PDFsharp. + /// + public object Tag + { + get { return _tag; } + set { _tag = value; } + } + object _tag; + + /// + /// Encapsulates the document's events. + /// + public DocumentEvents Events + { + get { return _events ?? (_events = new DocumentEvents()); } + } + DocumentEvents _events; + + /// + /// Gets or sets a value used to distinguish PdfDocument objects. + /// The name is not used by PDFsharp. + /// + string Name + { + get { return _name; } + set { _name = value; } + } + string _name = NewName(); + + /// + /// Get a new default name for a new document. + /// + static string NewName() + { +#if DEBUG_ + if (PdfDocument.nameCount == 57) + PdfDocument.nameCount.GetType(); +#endif + return "Document " + _nameCount++; + } + static int _nameCount; + + internal bool CanModify + { + //get {return _state == DocumentState.Created || _state == DocumentState.Modifyable;} + get { return true; } + } + + /// + /// Closes this instance. + /// + public void Close() + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + + if (_outStream != null) + { + // Get security handler if document gets encrypted + PdfStandardSecurityHandler securityHandler = null; + if (SecuritySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None) + securityHandler = SecuritySettings.SecurityHandler; + + PdfWriter writer = new PdfWriter(_outStream, securityHandler); + try + { + DoSave(writer); + } + finally + { + writer.Close(); + } + } + } + +#if true //!NETFX_CORE + /// + /// Saves the document to the specified path. If a file already exists, it will be overwritten. + /// + public void Save(string path) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + +#if !NETFX_CORE + using (Stream stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) + { + Save(stream); + } +#else + var task = SaveAsync(path, true); + + ////var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("MyWav.wav", Windows.Storage.CreationCollisionOption.ReplaceExisting); + ////var stream = file.OpenStreamForWriteAsync(); + ////var writer = new StreamWriter(stream); + ////Save(stream); + + //var ms = new MemoryStream(); + //Save(ms, false); + //byte[] pdf = ms.ToArray(); + //ms.Close(); +#endif + } +#endif + +#if NETFX_CORE + /// + /// Saves the document to the specified path. If a file already exists, it will be overwritten. + /// + public async Task SaveAsync(string path, bool closeStream) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + + // Just march through... + + var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("My1st.pdf", Windows.Storage.CreationCollisionOption.ReplaceExisting); + var stream = await file.OpenStreamForWriteAsync(); + using (var writer = new StreamWriter(stream)) + { + Save(stream, false); + } + + //var ms = new MemoryStream(); + //Save(ms, false); + //byte[] pdf = ms.ToArray(); + //ms.Close(); + //await stream.WriteAsync(pdf, 0, pdf.Length); + //stream.Close(); + } +#endif + + /// + /// Saves the document to the specified stream. + /// + public void Save(Stream stream, bool closeStream) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + + // TODO: more diagnostic checks + string message = ""; + if (!CanSave(ref message)) + throw new PdfSharpException(message); + + // Get security handler if document gets encrypted. + PdfStandardSecurityHandler securityHandler = null; + if (SecuritySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None) + securityHandler = SecuritySettings.SecurityHandler; + + PdfWriter writer = null; + try + { + writer = new PdfWriter(stream, securityHandler); + DoSave(writer); + } + finally + { + if (stream != null) + { + if (closeStream) +#if UWP + stream.Dispose(); +#else + stream.Close(); +#endif + else + { + if (stream.CanRead && stream.CanSeek) + stream.Position = 0; // Reset the stream position if the stream is kept open. + } + } + if (writer != null) + writer.Close(closeStream); + } + } + + /// + /// Saves the document to the specified stream. + /// The stream is not closed by this function. + /// (Older versions of PDFsharp closes the stream. That was not very useful.) + /// + public void Save(Stream stream) + { + Save(stream, false); + } + + /// + /// Implements saving a PDF file. + /// + void DoSave(PdfWriter writer) + { + if (_pages == null || _pages.Count == 0) + { + if (_outStream != null) + { + // Give feedback if the wrong constructor was used. + throw new InvalidOperationException("Cannot save a PDF document with no pages. Do not use \"public PdfDocument(string filename)\" or \"public PdfDocument(Stream outputStream)\" if you want to open an existing PDF document from a file or stream; use PdfReader.Open() for that purpose."); + } + throw new InvalidOperationException("Cannot save a PDF document with no pages."); + } + + try + { + // HACK: Remove XRefTrailer + if (_trailer is PdfCrossReferenceStream) + { + // HACK^2: Preserve the SecurityHandler. + PdfStandardSecurityHandler securityHandler = _securitySettings.SecurityHandler; + _trailer = new PdfTrailer((PdfCrossReferenceStream)_trailer); + _trailer._securityHandler = securityHandler; + } + + bool encrypt = _securitySettings.DocumentSecurityLevel != PdfDocumentSecurityLevel.None; + if (encrypt) + { + PdfStandardSecurityHandler securityHandler = _securitySettings.SecurityHandler; + if (securityHandler.Reference == null) + _irefTable.Add(securityHandler); + else + Debug.Assert(_irefTable.Contains(securityHandler.ObjectID)); + _trailer.Elements[PdfTrailer.Keys.Encrypt] = _securitySettings.SecurityHandler.Reference; + } + else + _trailer.Elements.Remove(PdfTrailer.Keys.Encrypt); + + PrepareForSave(); + + if (encrypt) + _securitySettings.SecurityHandler.PrepareEncryption(); + + writer.WriteFileHeader(this); + PdfReference[] irefs = _irefTable.AllReferences; + int count = irefs.Length; + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; +#if DEBUG_ + if (iref.ObjectNumber == 378) + GetType(); +#endif + iref.Position = writer.Position; + iref.Value.WriteObject(writer); + } + int startxref = writer.Position; + _irefTable.WriteObject(writer); + writer.WriteRaw("trailer\n"); + _trailer.Elements.SetInteger("/Size", count + 1); + _trailer.WriteObject(writer); + writer.WriteEof(this, startxref); + + //if (encrypt) + //{ + // state &= ~DocumentState.SavingEncrypted; + // //_securitySettings.SecurityHandler.EncryptDocument(); + //} + } + finally + { + if (writer != null) + { + writer.Stream.Flush(); + // DO NOT CLOSE WRITER HERE + //writer.Close(); + } + } + } + + /// + /// Dispatches PrepareForSave to the objects that need it. + /// + internal override void PrepareForSave() + { + PdfDocumentInformation info = Info; + + // Add patch level to producer if it is not '0'. + string pdfSharpProducer = VersionInfo.Producer; + if (!ProductVersionInfo.VersionPatch.Equals("0")) + pdfSharpProducer = ProductVersionInfo.Producer2; + + // Set Creator if value is undefined. + if (info.Elements[PdfDocumentInformation.Keys.Creator] == null) + info.Creator = pdfSharpProducer; + + // Keep original producer if file was imported. + string producer = info.Producer; + if (producer.Length == 0) + producer = pdfSharpProducer; + else + { + // Prevent endless concatenation if file is edited with PDFsharp more than once. + if (!producer.StartsWith(VersionInfo.Title)) + producer = pdfSharpProducer + " (Original: " + producer + ")"; + } + info.Elements.SetString(PdfDocumentInformation.Keys.Producer, producer); + + // Prepare used fonts. + if (_fontTable != null) + _fontTable.PrepareForSave(); + + // Let catalog do the rest. + Catalog.PrepareForSave(); + +#if true + // Remove all unreachable objects (e.g. from deleted pages) + int removed = _irefTable.Compact(); + if (removed != 0) + Debug.WriteLine("PrepareForSave: Number of deleted unreachable objects: " + removed); + _irefTable.Renumber(); +#endif + + // @PDF/UA + // Create PdfMetadata now to include the final document information in XMP generation. + Catalog.Elements.SetReference(PdfCatalog.Keys.Metadata, new PdfMetadata(this)); + } + + /// + /// Determines whether the document can be saved. + /// + public bool CanSave(ref string message) + { + if (!SecuritySettings.CanSave(ref message)) + return false; + + return true; + } + + internal bool HasVersion(string version) + { + return String.Compare(Catalog.Version, version) >= 0; + } + + /// + /// Gets the document options used for saving the document. + /// + public PdfDocumentOptions Options + { + get + { + if (_options == null) + _options = new PdfDocumentOptions(this); + return _options; + } + } + PdfDocumentOptions _options; + + /// + /// Gets PDF specific document settings. + /// + public PdfDocumentSettings Settings + { + get + { + if (_settings == null) + _settings = new PdfDocumentSettings(this); + return _settings; + } + } + PdfDocumentSettings _settings; + + /// + /// NYI Indicates whether large objects are written immediately to the output stream to relieve + /// memory consumption. + /// + internal bool EarlyWrite + { + get { return false; } + } + + /// + /// Gets or sets the PDF version number. Return value 14 e.g. means PDF 1.4 / Acrobat 5 etc. + /// + public int Version + { + get { return _version; } + set + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + if (value < 12 || value > 17) // TODO not really implemented + throw new ArgumentException(PSSR.InvalidVersionNumber, "value"); + _version = value; + } + } + internal int _version; + + /// + /// Gets the number of pages in the document. + /// + public int PageCount + { + get + { + if (CanModify) + return Pages.Count; + // PdfOpenMode is InformationOnly + PdfDictionary pageTreeRoot = (PdfDictionary)Catalog.Elements.GetObject(PdfCatalog.Keys.Pages); + return pageTreeRoot.Elements.GetInteger(PdfPages.Keys.Count); + } + } + + /// + /// Gets the file size of the document. + /// + public long FileSize + { + get { return _fileSize; } + } + internal long _fileSize; // TODO: make private + + /// + /// Gets the full qualified file name if the document was read form a file, or an empty string otherwise. + /// + public string FullPath + { + get { return _fullPath; } + } + internal string _fullPath = String.Empty; // TODO: make private + + /// + /// Gets a Guid that uniquely identifies this instance of PdfDocument. + /// + public Guid Guid + { + get { return _guid; } + } + Guid _guid = Guid.NewGuid(); + + internal DocumentHandle Handle + { + get + { + if (_handle == null) + _handle = new DocumentHandle(this); + return _handle; + } + } + DocumentHandle _handle; + + /// + /// Returns a value indicating whether the document was newly created or opened from an existing document. + /// Returns true if the document was opened with the PdfReader.Open function, false otherwise. + /// + public bool IsImported + { + get { return (_state & DocumentState.Imported) != 0; } + } + + /// + /// Returns a value indicating whether the document is read only or can be modified. + /// + public bool IsReadOnly + { + get { return (_openMode != PdfDocumentOpenMode.Modify); } + } + + internal Exception DocumentNotImported() + { + return new InvalidOperationException("Document not imported."); + } + + /// + /// Gets information about the document. + /// + public PdfDocumentInformation Info + { + get + { + if (_info == null) + _info = _trailer.Info; + return _info; + } + } + PdfDocumentInformation _info; // never changes if once created + + /// + /// This function is intended to be undocumented. + /// + public PdfCustomValues CustomValues + { + get + { + if (_customValues == null) + _customValues = PdfCustomValues.Get(Catalog.Elements); + return _customValues; + } + set + { + if (value != null) + throw new ArgumentException("Only null is allowed to clear all custom values."); + PdfCustomValues.Remove(Catalog.Elements); + _customValues = null; + } + } + PdfCustomValues _customValues; + + /// + /// Get the pages dictionary. + /// + public PdfPages Pages + { + get + { + if (_pages == null) + _pages = Catalog.Pages; + return _pages; + } + } + PdfPages _pages; // never changes if once created + + /// + /// Gets or sets a value specifying the page layout to be used when the document is opened. + /// + public PdfPageLayout PageLayout + { + get { return Catalog.PageLayout; } + set + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + Catalog.PageLayout = value; + } + } + + /// + /// Gets or sets a value specifying how the document should be displayed when opened. + /// + public PdfPageMode PageMode + { + get { return Catalog.PageMode; } + set + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + Catalog.PageMode = value; + } + } + + /// + /// Gets the viewer preferences of this document. + /// + public PdfViewerPreferences ViewerPreferences + { + get { return Catalog.ViewerPreferences; } + } + + /// + /// Gets the root of the outline (or bookmark) tree. + /// + public PdfOutlineCollection Outlines + { + get { return Catalog.Outlines; } + } + + /// + /// Get the AcroForm dictionary. + /// + public PdfAcroForm AcroForm + { + get { return Catalog.AcroForm; } + } + + /// + /// Gets or sets the default language of the document. + /// + public string Language + { + get { return Catalog.Language; } + set { Catalog.Language = value; } + } + + /// + /// Gets the security settings of this document. + /// + public PdfSecuritySettings SecuritySettings + { + get { return _securitySettings ?? (_securitySettings = new PdfSecuritySettings(this)); } + } + internal PdfSecuritySettings _securitySettings; + + /// + /// Gets the document font table that holds all fonts used in the current document. + /// + internal PdfFontTable FontTable + { + get { return _fontTable ?? (_fontTable = new PdfFontTable(this)); } + } + PdfFontTable _fontTable; + + /// + /// Gets the document image table that holds all images used in the current document. + /// + internal PdfImageTable ImageTable + { + get + { + if (_imageTable == null) + _imageTable = new PdfImageTable(this); + return _imageTable; + } + } + PdfImageTable _imageTable; + + /// + /// Gets the document form table that holds all form external objects used in the current document. + /// + internal PdfFormXObjectTable FormTable // TODO: Rename to ExternalDocumentTable. + { + get { return _formTable ?? (_formTable = new PdfFormXObjectTable(this)); } + } + PdfFormXObjectTable _formTable; + + /// + /// Gets the document ExtGState table that holds all form state objects used in the current document. + /// + internal PdfExtGStateTable ExtGStateTable + { + get { return _extGStateTable ?? (_extGStateTable = new PdfExtGStateTable(this)); } + } + PdfExtGStateTable _extGStateTable; + + /// + /// Gets the PdfCatalog of the current document. + /// + internal PdfCatalog Catalog + { + get { return _catalog ?? (_catalog = _trailer.Root); } + } + PdfCatalog _catalog; // never changes if once created + + /// + /// Gets the PdfInternals object of this document, that grants access to some internal structures + /// which are not part of the public interface of PdfDocument. + /// + public new PdfInternals Internals + { + get { return _internals ?? (_internals = new PdfInternals(this)); } + } + PdfInternals _internals; + + /// + /// Creates a new page and adds it to this document. + /// Depending of the IsMetric property of the current region the page size is set to + /// A4 or Letter respectively. If this size is not appropriate it should be changed before + /// any drawing operations are performed on the page. + /// + public PdfPage AddPage() + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + return Catalog.Pages.Add(); + } + + /// + /// Adds the specified page to this document. If the page is from an external document, + /// it is imported to this document. In this case the returned page is not the same + /// object as the specified one. + /// + public PdfPage AddPage(PdfPage page) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + return Catalog.Pages.Add(page); + } + + /// + /// Creates a new page and inserts it in this document at the specified position. + /// + public PdfPage InsertPage(int index) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + return Catalog.Pages.Insert(index); + } + + /// + /// Inserts the specified page in this document. If the page is from an external document, + /// it is imported to this document. In this case the returned page is not the same + /// object as the specified one. + /// + public PdfPage InsertPage(int index, PdfPage page) + { + if (!CanModify) + throw new InvalidOperationException(PSSR.CannotModify); + return Catalog.Pages.Insert(index, page); + } + + /// + /// Adds a named destination to the document. + /// + /// The Named Destination's name. + /// The page to navigate to. + /// The PdfNamedDestinationParameters defining the named destination's parameters. + public void AddNamedDestination(string destinationName, int destinationPage, PdfNamedDestinationParameters parameters) + { + Internals.Catalog.Names.AddNamedDestination(destinationName, destinationPage, parameters); + } + + /// + /// Adds an embedded file to the document. + /// + /// The name used to refer and to entitle the embedded file. + /// The path of the file to embed. + public void AddEmbeddedFile(string name, string path) + { + var stream = new FileStream(path, FileMode.Open); + AddEmbeddedFile(name, stream); + } + + /// + /// Adds an embedded file to the document. + /// + /// The name used to refer and to entitle the embedded file. + /// The stream containing the file to embed. + public void AddEmbeddedFile(string name, Stream stream) + { + Internals.Catalog.Names.AddEmbeddedFile(name, stream); + } + + /// + /// Flattens a document (make the fields non-editable). + /// + public void Flatten() + { + for (int idx = 0; idx < AcroForm.Fields.Count; idx++) + { + AcroForm.Fields[idx].ReadOnly = true; + } + } + + /// + /// Gets the security handler. + /// + public PdfStandardSecurityHandler SecurityHandler + { + get { return _trailer.SecurityHandler; } + } + + internal PdfTrailer _trailer; + internal PdfCrossReferenceTable _irefTable; + internal Stream _outStream; + + // Imported Document + internal Lexer _lexer; + + internal DateTime _creation; + + /// + /// Occurs when the specified document is not used anymore for importing content. + /// + internal void OnExternalDocumentFinalized(PdfDocument.DocumentHandle handle) + { + if (tls != null) + { + //PdfDocument[] documents = tls.Documents; + tls.DetachDocument(handle); + } + + if (_formTable != null) + _formTable.DetachDocument(handle); + } + + //internal static GlobalObjectTable Gob = new GlobalObjectTable(); + + /// + /// Gets the ThreadLocalStorage object. It is used for caching objects that should created + /// only once. + /// + internal static ThreadLocalStorage Tls + { + get { return tls ?? (tls = new ThreadLocalStorage()); } + } + [ThreadStatic] + static ThreadLocalStorage tls; + + [DebuggerDisplay("(ID={ID}, alive={IsAlive})")] + internal class DocumentHandle + { + public DocumentHandle(PdfDocument document) + { + _weakRef = new WeakReference(document); + ID = document._guid.ToString("B").ToUpper(); + } + + public bool IsAlive + { + get { return _weakRef.IsAlive; } + } + + public PdfDocument Target + { + get { return _weakRef.Target as PdfDocument; } + } + readonly WeakReference _weakRef; + + public string ID; + + public override bool Equals(object obj) + { + DocumentHandle handle = obj as DocumentHandle; + if (!ReferenceEquals(handle, null)) + return ID == handle.ID; + return false; + } + + public override int GetHashCode() + { + return ID.GetHashCode(); + } + + public static bool operator ==(DocumentHandle left, DocumentHandle right) + { + if (ReferenceEquals(left, null)) + return ReferenceEquals(right, null); + return left.Equals(right); + } + + public static bool operator !=(DocumentHandle left, DocumentHandle right) + { + return !(left == right); + } + } + +#pragma warning disable CS0649 + internal object _uaManager; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentInformation.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentInformation.cs new file mode 100644 index 00000000..11149571 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentInformation.cs @@ -0,0 +1,207 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Represents the PDF document information dictionary. + /// + public sealed class PdfDocumentInformation : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfDocumentInformation(PdfDocument document) + : base(document) + { } + + internal PdfDocumentInformation(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets the document's title. + /// + public string Title + { + get { return Elements.GetString(Keys.Title); } + set { Elements.SetString(Keys.Title, value); } + } + + /// + /// Gets or sets the name of the person who created the document. + /// + public string Author + { + get { return Elements.GetString(Keys.Author); } + set { Elements.SetString(Keys.Author, value); } + } + + /// + /// Gets or sets the name of the subject of the document. + /// + public string Subject + { + get { return Elements.GetString(Keys.Subject); } + set { Elements.SetString(Keys.Subject, value); } + } + + /// + /// Gets or sets keywords associated with the document. + /// + public string Keywords + { + get { return Elements.GetString(Keys.Keywords); } + set { Elements.SetString(Keys.Keywords, value); } + } + + /// + /// Gets or sets the name of the application (for example, MigraDoc) that created the document. + /// + public string Creator + { + get { return Elements.GetString(Keys.Creator); } + set { Elements.SetString(Keys.Creator, value); } + } + + /// + /// Gets the producer application (for example, PDFsharp). + /// + public string Producer + { + get { return Elements.GetString(Keys.Producer); } + } + + /// + /// Gets or sets the creation date of the document. + /// Breaking Change: If the date is not set in a PDF file DateTime.MinValue is returned. + /// + public DateTime CreationDate + { + get { return Elements.GetDateTime(Keys.CreationDate, DateTime.MinValue); } + set { Elements.SetDateTime(Keys.CreationDate, value); } + } + + /// + /// Gets or sets the modification date of the document. + /// Breaking Change: If the date is not set in a PDF file DateTime.MinValue is returned. + /// + public DateTime ModificationDate + { + get { return Elements.GetDateTime(Keys.ModDate, DateTime.MinValue); } + set { Elements.SetDateTime(Keys.ModDate, value); } + } + + // TODO CustomProperties and meta data + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + /// + /// (Optional; PDF 1.1) The documents title. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Title = "/Title"; + + /// + /// (Optional) The name of the person who created the document. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Author = "/Author"; + + /// + /// (Optional; PDF 1.1) The subject of the document. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Subject = "/Subject"; + + /// + /// (Optional; PDF 1.1) Keywords associated with the document. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Keywords = "/Keywords"; + + /// + /// (Optional) If the document was converted to PDF from another format, + /// the name of the application (for example, empira MigraDoc) that created the + /// original document from which it was converted. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Creator = "/Creator"; + + /// + /// (Optional) If the document was converted to PDF from another format, + /// the name of the application (for example, this library) that converted it to PDF. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string Producer = "/Producer"; + + /// + /// (Optional) The date and time the document was created, in human-readable form. + /// + [KeyInfo(KeyType.Date | KeyType.Optional)] + public const string CreationDate = "/CreationDate"; + + /// + /// (Required if PieceInfo is present in the document catalog; otherwise optional; PDF 1.1) + /// The date and time the document was most recently modified, in human-readable form. + /// + [KeyInfo(KeyType.String | KeyType.Optional)] + public const string ModDate = "/ModDate"; + + /// + /// (Optional; PDF 1.3) A name object indicating whether the document has been modified + /// to include trapping information. + /// + [KeyInfo("1.3", KeyType.Name | KeyType.Optional)] + public const string Trapped = "/Trapped"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentOptions.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentOptions.cs new file mode 100644 index 00000000..8c640f63 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentOptions.cs @@ -0,0 +1,111 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable ConvertToAutoProperty + +namespace PdfSharp.Pdf +{ + /// + /// Holds information how to handle the document when it is saved as PDF stream. + /// + public sealed class PdfDocumentOptions + { + internal PdfDocumentOptions(PdfDocument document) + { + //_deflateContents = true; + //_writeProcedureSets = true; + } + + /// + /// Gets or sets the color mode. + /// + public PdfColorMode ColorMode + { + get { return _colorMode; } + set { _colorMode = value; } + } + PdfColorMode _colorMode = PdfColorMode.Rgb; + + /// + /// Gets or sets a value indicating whether to compress content streams of PDF pages. + /// + public bool CompressContentStreams + { + get { return _compressContentStreams; } + set { _compressContentStreams = value; } + } +#if DEBUG + bool _compressContentStreams = false; +#else + bool _compressContentStreams = true; +#endif + + /// + /// Gets or sets a value indicating that all objects are not compressed. + /// + public bool NoCompression + { + get { return _noCompression; } + set { _noCompression = value; } + } + bool _noCompression; + + /// + /// Gets or sets the flate encode mode. Besides the balanced default mode you can set modes for best compression (slower) or best speed (larger files). + /// + public PdfFlateEncodeMode FlateEncodeMode + { + get { return _flateEncodeMode; } + set { _flateEncodeMode = value; } + } + PdfFlateEncodeMode _flateEncodeMode = PdfFlateEncodeMode.Default; + + /// + /// Gets or sets a value indicating whether to compress bilevel images using CCITT compression. + /// With true, PDFsharp will try FlateDecode CCITT and will use the smallest one or a combination of both. + /// With false, PDFsharp will always use FlateDecode only - files may be a few bytes larger, but file creation is faster. + /// + public bool EnableCcittCompressionForBilevelImages + { + get { return _enableCcittCompressionForBilevelImages; } + set { _enableCcittCompressionForBilevelImages = value; } + } + bool _enableCcittCompressionForBilevelImages = false; + + /// + /// Gets or sets a value indicating whether to compress JPEG images with the FlateDecode filter. + /// + public PdfUseFlateDecoderForJpegImages UseFlateDecoderForJpegImages + { + get { return _useFlateDecoderForJpegImages; } + set { _useFlateDecoderForJpegImages = value; } + } + PdfUseFlateDecoderForJpegImages _useFlateDecoderForJpegImages = PdfUseFlateDecoderForJpegImages.Never; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentSettings.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentSettings.cs new file mode 100644 index 00000000..64e5dfcb --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfDocumentSettings.cs @@ -0,0 +1,68 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Holds PDF specific information of the document. + /// + public sealed class PdfDocumentSettings + { + internal PdfDocumentSettings(PdfDocument document) + { } + + /// + /// Gets or sets the default trim margins. + /// + public TrimMargins TrimMargins + { + get + { + if (_trimMargins == null) + _trimMargins = new TrimMargins(); + return _trimMargins; + } + set + { + if (_trimMargins == null) + _trimMargins = new TrimMargins(); + if (value != null) + { + _trimMargins.Left = value.Left; + _trimMargins.Right = value.Right; + _trimMargins.Top = value.Top; + _trimMargins.Bottom = value.Bottom; + } + else + _trimMargins.All = 0; + } + } + TrimMargins _trimMargins = new TrimMargins(); + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfInteger.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfInteger.cs new file mode 100644 index 00000000..3ff572b4 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfInteger.cs @@ -0,0 +1,178 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a direct integer value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfInteger : PdfNumber, IConvertible + { + /// + /// Initializes a new instance of the class. + /// + public PdfInteger() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PdfInteger(int value) + { + _value = value; + } + + /// + /// Gets the value as integer. + /// + public int Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly int _value; + + /// + /// Returns the integer as string. + /// + public override string ToString() + { + return _value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Writes the integer as string. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + + #region IConvertible Members + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(_value); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return _value; + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + // TODO: Add PdfInteger.ToDateTime implementation + return new DateTime(); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return _value; + } + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(_value); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return _value; + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(_value); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(_value); + } + + string IConvertible.ToString(IFormatProvider provider) + { + return _value.ToString(provider); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(_value); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(_value); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return _value; + } + + /// + /// Returns TypeCode for 32-bit integers. + /// + public TypeCode GetTypeCode() + { + return TypeCode.Int32; + } + + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return _value; + } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + // TODO: Add PdfInteger.ToType implementation + return null; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(_value); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfIntegerObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfIntegerObject.cs new file mode 100644 index 00000000..e45ec1b1 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfIntegerObject.cs @@ -0,0 +1,94 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect integer value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfIntegerObject : PdfNumberObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfIntegerObject() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfIntegerObject(int value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class. + /// + public PdfIntegerObject(PdfDocument document, int value) + : base(document) + { + _value = value; + } + + /// + /// Gets the value as integer. + /// + public int Value + { + get { return _value; } + //set {_value = value;} + } + readonly int _value; + + /// + /// Returns the integer as string. + /// + public override string ToString() + { + return _value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Writes the integer literal. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(_value); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfItem.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfItem.cs new file mode 100644 index 00000000..fb0dfd4a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfItem.cs @@ -0,0 +1,69 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// The base class of all PDF objects and simple PDF types. + /// + public abstract class PdfItem : ICloneable + { + // All simple types (i.e. derived from PdfItem but not from PdfObject) must be immutable. + + object ICloneable.Clone() + { + return Copy(); + } + + /// + /// Creates a copy of this object. + /// + public PdfItem Clone() + { + return (PdfItem)Copy(); + } + + /// + /// Implements the copy mechanism. Must be overridden in derived classes. + /// + protected virtual object Copy() + { + return MemberwiseClone(); + } + + /// + /// When overridden in a derived class, appends a raw string representation of this object + /// to the specified PdfWriter. + /// + internal abstract void WriteObject(PdfWriter writer); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfLiteral.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfLiteral.cs new file mode 100644 index 00000000..96f4bf5e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfLiteral.cs @@ -0,0 +1,101 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Globalization; +using System.Text; +using System.IO; +using PdfSharp.Drawing; +using PdfSharp.Internal; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Represents text that is written 'as it is' into the PDF stream. This class can lead to invalid PDF files. + /// E.g. strings in a literal are not encrypted when the document is saved with a password. + /// + public sealed class PdfLiteral : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + public PdfLiteral() + { } + + /// + /// Initializes a new instance with the specified string. + /// + public PdfLiteral(string value) + { + _value = value; + } + + /// + /// Initializes a new instance with the culture invariant formatted specified arguments. + /// + public PdfLiteral(string format, params object[] args) + { + _value = PdfEncoders.Format(format, args); + } + + /// + /// Creates a literal from an XMatrix + /// + public static PdfLiteral FromMatrix(XMatrix matrix) + { + return new PdfLiteral("[" + PdfEncoders.ToString(matrix) + "]"); + } + + /// + /// Gets the value as litaral string. + /// + public string Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly string _value = String.Empty; + + /// + /// Returns a string that represents the current value. + /// + public override string ToString() + { + return _value; + } + + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfMetadata.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfMetadata.cs new file mode 100644 index 00000000..6b4eafa9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfMetadata.cs @@ -0,0 +1,147 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an XML Metadata stream. + /// + public sealed class PdfMetadata : PdfDictionary + { + /// + /// Initializes a new instance of the class. + /// + public PdfMetadata() + { + Elements.SetName(Keys.Type, "/Metadata"); + Elements.SetName(Keys.Subtype, "/XML"); + SetupStream(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document that owns this object. + public PdfMetadata(PdfDocument document) + : base(document) + { + document.Internals.AddObject(this); + Elements.SetName(Keys.Type, "/Metadata"); + Elements.SetName(Keys.Subtype, "/XML"); + SetupStream(); + } + + void SetupStream() + { + var stream = GenerateXmp(); + + byte[] bytes = PdfEncoders.RawEncoding.GetBytes(stream); + CreateStream(bytes); + } + + string GenerateXmp() + { + var instanceId = Guid.NewGuid().ToString(); + var documentId = Guid.NewGuid().ToString(); + + var creationDate = _document.Info.CreationDate.ToString("o"); + var modificationDate = _document.Info.CreationDate.ToString("o"); + + var creator = _document.Info.Creator; + var producer = _document.Info.Producer; + var title = _document.Info.Title; + + // XMP Documentation: http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/XMP%20SDK%20Release%20cc-2016-08/XMPSpecificationPart1.pdf + + var str = + // UTF-8 Byte order mark "" and GUID (like in Reference) to avoid accidental usage in data stream. + "\n" + + + " \n" + + " \n" + + " \n" + + + " uuid:" + instanceId + "\n" + + " uuid:" + documentId + "\n" + + + " \n" + + " \n" + + " 1\n" + + " \n" + + " \n" + + + " " + creationDate + "\n" + + " " + modificationDate + "\n" + + " " + creator + "\n" + + " " + modificationDate + "\n" + + + " \n" + + " \n" + + + " " + producer + "\n" + + + " \n" + + " \n" + + " \n" + + " \n" + + + " " + title + "\n" + + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + + return str; + } + + /// + /// Predefined keys of this dictionary. + /// + internal class Keys : KeysBase + { + /// + /// (Required) The type of PDF object that this dictionary describes; must be Metadata for a metadata stream. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Metadata")] + public const string Type = "/Type"; + + /// + /// (Required) The type of metadata stream that this dictionary describes; must be XML. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "XML")] + public const string Subtype = "/Subtype"; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfName.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfName.cs new file mode 100644 index 00000000..26114d64 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfName.cs @@ -0,0 +1,170 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a PDF name value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfName : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + public PdfName() + { + _value = "/"; // Empty name. + } + + /// + /// Initializes a new instance of the class. + /// Parameter value always must start with a '/'. + /// + public PdfName(string value) + { + if (value == null) + throw new ArgumentNullException("value"); + if (value.Length == 0 || value[0] != '/') + throw new ArgumentException(PSSR.NameMustStartWithSlash); + + _value = value; + } + + /// + /// Determines whether the specified object is equal to this name. + /// + public override bool Equals(object obj) + { + return _value.Equals(obj); + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + /// + /// Gets the name as a string. + /// + public string Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly string _value; + + /// + /// Returns the name. The string always begins with a slash. + /// + public override string ToString() + { + return _value; + } + + /// + /// Determines whether the specified name and string are equal. + /// + public static bool operator ==(PdfName name, string str) + { + if (ReferenceEquals(name, null)) + return str == null; + + return name._value == str; + } + + /// + /// Determines whether the specified name and string are not equal. + /// + public static bool operator !=(PdfName name, string str) + { + if (ReferenceEquals(name, null)) + return str != null; + + return name._value != str; + } + + /// + /// Represents the empty name. + /// + public static readonly PdfName Empty = new PdfName("/"); + + /// + /// Writes the name including the leading slash. + /// + internal override void WriteObject(PdfWriter writer) + { + // TODO: what if unicode character are part of the name? + writer.Write(this); + } + + /// + /// Gets the comparer for this type. + /// + public static PdfXNameComparer Comparer + { + get { return new PdfXNameComparer(); } + } + + /// + /// Implements a comparer that compares PdfName objects. + /// + public class PdfXNameComparer : IComparer + { + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + public int Compare(PdfName l, PdfName r) + { +#if true_ +#else + if (l != null) + { + if (r != null) + return String.Compare(l._value, r._value, StringComparison.Ordinal); + return -1; + } + if (r != null) + return 1; + return 0; +#endif + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNameObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNameObject.cs new file mode 100644 index 00000000..c4fdb585 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNameObject.cs @@ -0,0 +1,151 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect name value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. Acrobat sometime uses indirect + /// names to save space, because an indirect reference to a name may be shorter than a long name. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfNameObject : PdfObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfNameObject() + { + _value = "/"; // Empty name. + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + /// The value. + public PdfNameObject(PdfDocument document, string value) + : base(document) + { + if (value == null) + throw new ArgumentNullException("value"); + if (value.Length == 0 || value[0] != '/') + throw new ArgumentException(PSSR.NameMustStartWithSlash); + + _value = value; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + public override bool Equals(object obj) + { + return _value.Equals(obj); + } + + /// + /// Serves as a hash function for this type. + /// + public override int GetHashCode() + { + return _value.GetHashCode(); + } + + /// + /// Gets or sets the name value. + /// + public string Value + { + get { return _value; } + set { _value = value; } + } + string _value; + + /// + /// Returns the name. The string always begins with a slash. + /// + public override string ToString() + { + // TODO: Encode characters. + return _value; + } + + /// + /// Determines whether a name is equal to a string. + /// + public static bool operator ==(PdfNameObject name, string str) + { + return name._value == str; + } + + /// + /// Determines whether a name is not equal to a string. + /// + public static bool operator !=(PdfNameObject name, string str) + { + return name._value != str; + } + +#if leads_to_ambiguity + public static bool operator ==(string str, PdfName name) + { + return str == name.value; + } + + public static bool operator !=(string str, PdfName name) + { + return str == name.value; + } + + public static bool operator ==(PdfName name1, PdfName name2) + { + return name1.value == name2.value; + } + + public static bool operator !=(PdfName name1, PdfName name2) + { + return name1.value != name2.value; + } +#endif + + /// + /// Writes the name including the leading slash. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(new PdfName(_value)); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNameTreeNode.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNameTreeNode.cs new file mode 100644 index 00000000..41705472 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNameTreeNode.cs @@ -0,0 +1,252 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a name tree node. + /// + [DebuggerDisplay("({DebuggerDisplay})")] + public sealed class PdfNameTreeNode : PdfDictionary + { + // Reference: 3.8.5 Name Trees / Page 161 + + /// + /// Initializes a new instance of the class. + /// + public PdfNameTreeNode() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfNameTreeNode(bool isRoot) //??? + { + IsRoot = isRoot; + } + + /// + /// Gets a value indicating whether this instance is a root node. + /// + public bool IsRoot + { + get { return _isRoot; } + private set { _isRoot = value; } + } + bool _isRoot; + + /// + /// Gets the number of Kids elements. + /// + public int KidsCount + { + get + { + PdfArray kids = Elements.GetArray(Keys.Kids); + return kids != null ? kids.Elements.Count : 0; + } + } + + /// + /// Gets the number of Names elements. + /// + public int NamesCount + { + get + { + PdfArray names = Elements.GetArray(Keys.Names); + // Entries are key / value pairs, so divide by 2. + return names != null ? names.Elements.Count / 2 : 0; + } + } + + /// + /// Adds a child node to this node. + /// + public void AddKid(PdfNameTreeNode kidNode) + { + PdfArray kids = Elements.GetArray(Keys.Kids); + if (kids == null) + { + kids = new PdfArray(); + Elements.SetObject(Keys.Kids, kids); + } + kids.Elements.Add(kidNode); + _updateRequired = true; + } + + /// + /// Adds a key/value pair to the Names array of this node. + /// + public void AddName(string key, PdfItem value) + { + PdfArray names = Elements.GetArray(Keys.Names); + if (names == null) + { + names = new PdfArray(); + Elements.SetObject(Keys.Names, names); + } + + // Insert names sorted by key. + int i = 0; + while (i < names.Elements.Count && string.CompareOrdinal(names.Elements.GetString(i), key) < 0) + // Entries are key / value pairs, so add 2. + i += 2; + + names.Elements.Insert(i, new PdfString(key)); + names.Elements.Insert(i + 1, value); + _updateRequired = true; + } + + /// + /// Gets the least key. + /// + public string LeastKey + { + get { return "todo"; } + } + + /// + /// Gets the greatest key. + /// + public string GreatestKey + { + get { return "todo"; } + } + + /// + /// Updates the limits by inspecting Kids and Names. + /// + void UpdateLimits() + { + if (_updateRequired) + { + //todo Recalc Limits + _updateRequired = false; + } + } + bool _updateRequired; + + internal override void PrepareForSave() + { + UpdateLimits(); + // Check consistence... + base.PrepareForSave(); + } + + internal override void WriteObject(PdfWriter writer) + { + GetType(); + base.WriteObject(writer); + } + + ///// + ///// Returns the value in the PDF date format. + ///// + //public override string ToString() + //{ + // string delta = _value.ToString("zzz").Replace(':', '\''); + // return String.Format("D:{0:yyyyMMddHHmmss}{1}'", _value, delta); + //} + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + // Reference: TABLE 3.33 Entries in a name tree node dictionary / Page 162 + + // ReSharper disable InconsistentNaming + + /// + /// (Root and intermediate nodes only; required in intermediate nodes; + /// present in the root node if and only if Names is not present) + /// An array of indirect references to the immediate children of this node + /// The children may be intermediate or leaf nodes. + /// + [KeyInfo(KeyType.Array)] + public const string Kids = "/Kids"; + + /// + /// (Root and leaf nodes only; required in leaf nodes; present in the root node if and only if Kidsis not present) + /// An array of the form + /// [key1 value1 key2 value2 keyn valuen] + /// where each keyi is a string and the corresponding valuei is the object associated with that key. + /// The keys are sorted in lexical order, as described below. + /// + [KeyInfo(KeyType.Array)] + public const string Names = "/Names"; + + /// + /// (Intermediate and leaf nodes only; required) + /// An array of two strings, specifying the (lexically) least and greatest keys included in the Names array + /// of a leaf node or in the Namesarrays of any leaf nodes that are descendants of an intermediate node. + /// + [KeyInfo(KeyType.Array)] + public const string Limits = "/Limits"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + + // ReSharper restore InconsistentNaming + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + /// The debugger display. + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + return String.Format("root:{0}", _isRoot); + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNull.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNull.cs new file mode 100644 index 00000000..9513d64c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNull.cs @@ -0,0 +1,66 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a indirect reference that is not in the cross reference table. + /// + public sealed class PdfNull : PdfItem + { + // Reference: 3.2.8 Null Object / Page 63 + + PdfNull() + { } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + return "null"; + } + + internal override void WriteObject(PdfWriter writer) + { + // Implementet because it must be overridden. + writer.WriteRaw(" null "); + } + + /// + /// The only instance of this class. + /// + public static readonly PdfNull Value = new PdfNull(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNullObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNullObject.cs new file mode 100644 index 00000000..215ab11b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNullObject.cs @@ -0,0 +1,74 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect null value. This type is not used by PDFsharp, but at least + /// one tool from Adobe creates PDF files with a null object. + /// + public sealed class PdfNullObject : PdfObject + { + // Reference: 3.2.8 Null Object / Page 63 + + /// + /// Initializes a new instance of the class. + /// + public PdfNullObject() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfNullObject(PdfDocument document) + : base(document) + { } + + /// + /// Returns the string "null". + /// + public override string ToString() + { + return "null"; + } + + /// + /// Writes the keyword null. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.WriteRaw(" null "); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNumber.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumber.cs new file mode 100644 index 00000000..cd60f7cf --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumber.cs @@ -0,0 +1,39 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Base class for direct number values (not yet used, maybe superfluous). + /// + public abstract class PdfNumber : PdfItem + { + // No code in base class. + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberObject.cs new file mode 100644 index 00000000..07438c15 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberObject.cs @@ -0,0 +1,51 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Base class for indirect number values (not yet used, maybe superfluous). + /// + public abstract class PdfNumberObject : PdfObject + { + /// + /// Initializes a new instance of the class. + /// + protected PdfNumberObject() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + protected PdfNumberObject(PdfDocument document) + : base(document) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberTreeNode.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberTreeNode.cs new file mode 100644 index 00000000..4df7d8c6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfNumberTreeNode.cs @@ -0,0 +1,224 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.Annotations; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a number tree node. + /// + [DebuggerDisplay("({DebuggerDisplay})")] + public sealed class PdfNumberTreeNode : PdfDictionary + { + // Reference: 3.8.6 Number Trees / Page 166 + + /// + /// Initializes a new instance of the class. + /// + public PdfNumberTreeNode() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfNumberTreeNode(bool isRoot) //??? Needed HACK StLa + { + IsRoot = isRoot; + } + + /// + /// Gets a value indicating whether this instance is a root node. + /// + public bool IsRoot + { + get { return _isRoot; } + private set { _isRoot = value; } + } + bool _isRoot; + + /// + /// Gets the number of Kids elements. + /// + public int KidsCount + { + get + { + PdfArray kids = Elements.GetArray(Keys.Kids); + return kids != null ? kids.Elements.Count : 0; + } + } + + /// + /// Gets the number of Nums elements. + /// + public int NumsCount + { + get + { + PdfArray names = Elements.GetArray(Keys.Nums); + // Entries are key / value pairs, so divide by 2. + return names != null ? names.Elements.Count / 2 : 0; + } + } + + /// + /// Adds a child node to this node. + /// + public void AddKid(PdfNumberTreeNode kidNode) + { + PdfArray kids = Elements.GetArray(Keys.Kids); + if (kids == null) + { + kids = new PdfArray(); + Elements.SetObject(Keys.Kids, kids); + } + kids.Elements.Add(kidNode); + _updateRequired = true; + } + + /// + /// Adds a key/value pair to the Nums array of this node. + /// + public void AddNumber(int key, PdfObject value) + { + PdfArray nums = Elements.GetArray(Keys.Nums); + if (nums == null) + { + nums = new PdfArray(); + Elements.SetObject(Keys.Nums, nums); + } + nums.Elements.Add(new PdfInteger(key)); + nums.Elements.Add(value); + _updateRequired = true; + } + + /// + /// Gets the least key. + /// + public string LeastKey + { + get { return "todo"; } + } + + /// + /// Gets the greatest key. + /// + public string GreatestKey + { + get { return "todo"; } + } + + /// + /// Updates the limits by inspecting Kids and Names. + /// + void UpdateLimits() + { + if (_updateRequired) + { + //todo Recalc Limits + _updateRequired = false; + } + } + bool _updateRequired; + + internal override void PrepareForSave() + { + UpdateLimits(); + // Check consistence... + base.PrepareForSave(); + } + + internal override void WriteObject(PdfWriter writer) + { + GetType(); + base.WriteObject(writer); + } + + ///// + ///// Returns the value in the PDF date format. + ///// + //public override string ToString() + //{ + // string delta = _value.ToString("zzz").Replace(':', '\''); + // return String.Format("D:{0:yyyyMMddHHmmss}{1}'", _value, delta); + //} + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + // Reference: TABLE 3.34 Entries in a number tree node dictionary / Page 166 + + // ReSharper disable InconsistentNaming + + /// + /// (Root and intermediate nodes only; required in intermediate nodes; + /// present in the root node if and only if Nums is not present) + /// An array of indirect references to the immediate children of this node. + /// The children may be intermediate or leaf nodes. + /// + [KeyInfo(KeyType.Array)] + public const string Kids = "/Kids"; + + /// + /// (Root and leaf nodes only; required in leaf nodes; present in the root node if and only if Kidsis not present) + /// An array of the form + /// [key1 value1 key2 value2 keyn valuen] + /// where each keyi is an integer and the corresponding valuei is the object associated with that key. + /// The keys are sorted in numerical order, analogously to the arrangement of keys in a name tree. + /// + [KeyInfo(KeyType.Array)] + public const string Nums = "/Nums"; + + /// + /// (Intermediate and leaf nodes only; required) + /// An array of two integers, specifying the (numerically) least and greatest keys included in the Nums array + /// of a leaf node or in the Nums arrays of any leaf nodes that are descendants of an intermediate node. + /// + [KeyInfo(KeyType.Array)] + public const string Limits = "/Limits"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + + // ReSharper restore InconsistentNaming + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfObject.cs new file mode 100644 index 00000000..ee303d3a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfObject.cs @@ -0,0 +1,602 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Base class of all composite PDF objects. + /// + public abstract class PdfObject : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + protected PdfObject() + { } + + /// + /// Initializes a new instance of the class. + /// + protected PdfObject(PdfDocument document) + { + // Calling a virtual member in a constructor is dangerous. + // In PDFsharp Document is overridden in PdfPage and the code is checked to be save + // when called for a not completely initialized object. + Document = document; + } + + /// + /// Initializes a new instance from an existing object. Used for object type transformation. + /// + protected PdfObject(PdfObject obj) + : this(obj.Owner) + { + // If the object that was transformed to an instance of a derived class was an indirect object + // set the value of the reference to this. + if (obj._iref != null) + obj._iref.Value = this; +#if DEBUG_ // BUG + else + { + // If this occurs it is an internal error + Debug.Assert(false, "Object type transformation must not be done with direct objects"); + } +#endif + } + + /// + /// Creates a copy of this object. The clone does not belong to a document, i.e. its owner and its iref are null. + /// + public new PdfObject Clone() + { + return (PdfObject)Copy(); + } + + /// + /// Implements the copy mechanism. Must be overridden in derived classes. + /// + protected override object Copy() + { + PdfObject obj = (PdfObject)base.Copy(); + obj._document = null; + obj._iref = null; + return obj; + } + +#if true_ // works, but may lead to other problems that I cannot assess + /// + /// Determines whether the specified object is equal to the current PdfObject. + /// + public override bool Equals(object obj) + { + if (obj is PdfObject) + { + PdfObject other = (PdfObject)obj; + // Take object type transformation into account + if (_iref != null && other._iref != null) + { + Debug.Assert(_iref.Value != null, "iref without value."); + Debug.Assert(other.iref.Value != null, "iref without value."); + return Object.ReferenceEquals(_iref.Value, other.iref.Value); + } + } + return base.Equals(obj); + } + + public override int GetHashCode() + { + if (_iref != null) + { + Debug.Assert(_iref.Value != null, "iref without value."); + return _iref.GetHashCode(); + } + return base.GetHashCode(); + } +#endif + + /// + /// Sets the object and generation number. + /// Setting the object identifier makes this object an indirect object, i.e. the object gets + /// a PdfReference entry in the PdfReferenceTable. + /// + internal void SetObjectID(int objectNumber, int generationNumber) + { + PdfObjectID objectID = new PdfObjectID(objectNumber, generationNumber); + + // TODO: check imported + if (_iref == null) + _iref = _document._irefTable[objectID]; + if (_iref == null) + { + // ReSharper disable once ObjectCreationAsStatement because the new object is set to this object + // in the constructor of PdfReference. + new PdfReference(this); + Debug.Assert(_iref != null); + _iref.ObjectID = objectID; + } + _iref.Value = this; + _iref.Document = _document; + } + + /// + /// Gets the PdfDocument this object belongs to. + /// + public virtual PdfDocument Owner + { + get { return _document; } + } + + /// + /// Sets the PdfDocument this object belongs to. + /// + internal virtual PdfDocument Document + { + set + { + if (!ReferenceEquals(_document, value)) + { + if (_document != null) + throw new InvalidOperationException("Cannot change document."); + _document = value; + if (_iref != null) + _iref.Document = value; + } + } + } + internal PdfDocument _document; + + /// + /// Indicates whether the object is an indirect object. + /// + public bool IsIndirect + { + // An object is an indirect object if and only if is has an indirect reference value. + get { return _iref != null; } + } + + /// + /// Gets the PdfInternals object of this document, that grants access to some internal structures + /// which are not part of the public interface of PdfDocument. + /// + public PdfObjectInternals Internals + { + get { return _internals ?? (_internals = new PdfObjectInternals(this)); } + } + PdfObjectInternals _internals; + + /// + /// When overridden in a derived class, prepares the object to get saved. + /// + internal virtual void PrepareForSave() + { } + + /// + /// Saves the stream position. 2nd Edition. + /// + internal override void WriteObject(PdfWriter writer) + { + Debug.Assert(false, "Must not come here!"); + //Debug.Assert(_inStreamOffset <= 0); + //if (_inStreamOffset == 0) + //{ + // //_InStreamOffset = stream.Position; + // _document.xrefTable.AddObject(this); + // return Format("{0} {1} obj\n", _objectID, _generation); + //} + //else if (_inStreamOffset == -1) + //{ + //} + //return null; + } + + /// + /// Gets the object identifier. Returns PdfObjectID.Empty for direct objects, + /// i.e. never returns null. + /// + internal PdfObjectID ObjectID + { + get { return _iref != null ? _iref.ObjectID : PdfObjectID.Empty; } + } + + /// + /// Gets the object number. + /// + internal int ObjectNumber + { + get { return ObjectID.ObjectNumber; } + } + + /// + /// Gets the generation number. + /// + internal int GenerationNumber + { + get { return ObjectID.GenerationNumber; } + } + + ///// + ///// Creates a deep copy of the specified value and its transitive closure and adds the + ///// new objects to the specified owner document. + ///// + /// The document that owns the cloned objects. + /// The root object to be cloned. + /// The clone of the root object + internal static PdfObject DeepCopyClosure(PdfDocument owner, PdfObject externalObject) + { + // Get transitive closure. + PdfObject[] elements = externalObject.Owner.Internals.GetClosure(externalObject); + int count = elements.Length; +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + Debug.Assert(elements[idx].XRef != null); + Debug.Assert(elements[idx].XRef.Document != null); + Debug.Assert(elements[idx].Document != null); + if (elements[idx].ObjectID.ObjectNumber == 12) + GetType(); + } +#endif + // 1st loop. Replace all objects by their clones. + PdfImportedObjectTable iot = new PdfImportedObjectTable(owner, externalObject.Owner); + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = elements[idx]; + PdfObject clone = obj.Clone(); + Debug.Assert(clone.Reference == null); + clone.Document = owner; + if (obj.Reference != null) + { + // Case: The cloned object was an indirect object. + // Add clone to new owner document. + owner._irefTable.Add(clone); + // The clone gets an iref by adding it to its new owner. + Debug.Assert(clone.Reference != null); + // Save an association from old object identifier to new iref. + iot.Add(obj.ObjectID, clone.Reference); + } + else + { + // Case: The cloned object was an direct object. + // Only the root object can be a direct object. + Debug.Assert(idx == 0); + } + // Replace external object by its clone. + elements[idx] = clone; + } +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + Debug.Assert(elements[idx]._iref != null); + Debug.Assert(elements[idx]._iref.Document != null); + Debug.Assert(resources[idx].Document != null); + if (elements[idx].ObjectID.ObjectNumber == 12) + GetType(); + } +#endif + + // 2nd loop. Fix up all indirect references that still refers to the import document. + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = elements[idx]; + Debug.Assert(obj.Owner == owner); + FixUpObject(iot, owner, obj); + } + + // Return the clone of the former root object. + return elements[0]; + } + + ///// + ///// Imports an object and its transitive closure to the specified document. + ///// + /// The imported object table of the owner for the external document. + /// The document that owns the cloned objects. + /// The root object to be cloned. + /// The clone of the root object + internal static PdfObject ImportClosure(PdfImportedObjectTable importedObjectTable, PdfDocument owner, PdfObject externalObject) + { + Debug.Assert(ReferenceEquals(importedObjectTable.Owner, owner), "importedObjectTable does not belong to the owner."); + Debug.Assert(ReferenceEquals(importedObjectTable.ExternalDocument, externalObject.Owner), + "The ExternalDocument of the importedObjectTable does not belong to the owner of object to be imported."); + + // Get transitive closure of external object. + PdfObject[] elements = externalObject.Owner.Internals.GetClosure(externalObject); + int count = elements.Length; +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + Debug.Assert(elements[idx].XRef != null); + Debug.Assert(elements[idx].XRef.Document != null); + Debug.Assert(elements[idx].Document != null); + if (elements[idx].ObjectID.ObjectNumber == 12) + GetType(); + } +#endif + // 1st loop. Already imported objects are reused and new ones are cloned. + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = elements[idx]; + Debug.Assert(!ReferenceEquals(obj.Owner, owner)); + + if (importedObjectTable.Contains(obj.ObjectID)) + { +#if DEBUG_ + if (obj.ObjectID.ObjectNumber == 5894) + obj.GetType(); +#endif + // Case: External object was already imported. + PdfReference iref = importedObjectTable[obj.ObjectID]; + Debug.Assert(iref != null); + Debug.Assert(iref.Value != null); + Debug.Assert(iref.Document == owner); + // Replace external object by the already cloned counterpart. + elements[idx] = iref.Value; + } + else + { + // Case: External object was not yet imported earlier and must be cloned. + PdfObject clone = obj.Clone(); + Debug.Assert(clone.Reference == null); + clone.Document = owner; + if (obj.Reference != null) + { + // Case: The cloned object was an indirect object. + // Add clone to new owner document. + owner._irefTable.Add(clone); + Debug.Assert(clone.Reference != null); + // Save an association from old object identifier to new iref. + importedObjectTable.Add(obj.ObjectID, clone.Reference); + } + else + { + // Case: The cloned object was a direct object. + // Only the root object can be a direct object. + Debug.Assert(idx == 0); + } + // Replace external object by its clone. + elements[idx] = clone; + } + } +#if DEBUG_ + for (int idx = 0; idx < count; idx++) + { + //Debug.Assert(elements[idx].Reference != null); + //Debug.Assert(elements[idx].Reference.Document != null); + Debug.Assert(elements[idx].IsIndirect == false); + Debug.Assert(elements[idx].Owner != null); + //if (elements[idx].ObjectID.ObjectNumber == 12) + // GetType(); + } +#endif + // 2nd loop. Fix up indirect references that still refers to the external document. + for (int idx = 0; idx < count; idx++) + { + PdfObject obj = elements[idx]; + Debug.Assert(owner != null); + FixUpObject(importedObjectTable, importedObjectTable.Owner, obj); + } + + // Return the imported root object. + return elements[0]; + } + + /// + /// Replace all indirect references to external objects by their cloned counterparts + /// owned by the importer document. + /// + static void FixUpObject(PdfImportedObjectTable iot, PdfDocument owner, PdfObject value) + { + Debug.Assert(ReferenceEquals(iot.Owner, owner)); + + PdfDictionary dict; + PdfArray array; + if ((dict = value as PdfDictionary) != null) + { + // Case: The object is a dictionary. + // Set document for cloned direct objects. + if (dict.Owner == null) + { + // If the dictionary has not yet an owner set the owner to the importing document. + dict.Document = owner; + } + else + { + // If the dictionary already has an owner it must be the importing document. + Debug.Assert(dict.Owner == owner); + } + + // Search for indirect references in all dictionary elements. + PdfName[] names = dict.Elements.KeyNames; + foreach (PdfName name in names) + { + PdfItem item = dict.Elements[name]; + Debug.Assert(item != null, "A dictionary element cannot be null."); + + // Is item an iref? + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Case: The item is a reference. + // Does the iref already belongs to the new owner? + if (iref.Document == owner) + { + // Yes: fine. Happens when an already cloned object is reused. + continue; + } + + //Debug.Assert(iref.Document == iot.Document); + // No: Replace with iref of cloned object. + PdfReference newXRef = iot[iref.ObjectID]; // TODO: Explain this line of code in all details. + Debug.Assert(newXRef != null); + Debug.Assert(newXRef.Document == owner); + dict.Elements[name] = newXRef; + } + else + { + // Case: The item is not a reference. + // If item is an object recursively fix its inner items. + PdfObject pdfObject = item as PdfObject; + if (pdfObject != null) + { + // Fix up inner objects, i.e. recursively walk down the object tree. + FixUpObject(iot, owner, pdfObject); + } + else + { + // The item is something else, e.g. a name. + // Nothing to do. + + // ...but let's double check this case in DEBUG build. + DebugCheckNonObjects(item); + } + } + } + } + else if ((array = value as PdfArray) != null) + { + // Case: The object is an array. + // Set document for cloned direct objects. + if (array.Owner == null) + { + // If the array has not yet an owner set the owner to the importing document. + array.Document = owner; + } + else + { + // If the array already has an owner it must be the importing document. + Debug.Assert(array.Owner == owner); + } + + // Search for indirect references in all array elements. + int count = array.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfItem item = array.Elements[idx]; + Debug.Assert(item != null, "An array element cannot be null."); + + // Is item an iref? + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Case: The item is a reference. + // Does the iref already belongs to the owner? + if (iref.Document == owner) + { + // Yes: fine. Happens when an already cloned object is reused. + continue; + } + + // No: replace with iref of cloned object. + Debug.Assert(iref.Document == iot.ExternalDocument); + PdfReference newXRef = iot[iref.ObjectID]; + Debug.Assert(newXRef != null); + Debug.Assert(newXRef.Document == owner); + array.Elements[idx] = newXRef; + } + else + { + // Case: The item is not a reference. + // If item is an object recursively fix its inner items. + PdfObject pdfObject = item as PdfObject; + if (pdfObject != null) + { + // Fix up inner objects, i.e. recursively walk down the object tree. + FixUpObject(iot, owner, pdfObject); + } + else + { + // The item is something else, e.g. a name. + // Nothing to do. + + // ...but let's double check this case in DEBUG build. + DebugCheckNonObjects(item); + } + } + } + } + else + { + // Case: The item is some other indirect object. + // Indirect integers, booleans, etc. are allowed, but PDFsharp do not create them. + // If such objects occur in imported PDF files from other producers, nothing more is to do. + // The owner was already set, which is double checked by the assertions below. + if (value is PdfNameObject || value is PdfStringObject || value is PdfBooleanObject || value is PdfIntegerObject || value is PdfNumberObject) + { + Debug.Assert(value.IsIndirect); + Debug.Assert(value.Owner == owner); + } + else + Debug.Assert(false, "Should not come here. Object is neither a dictionary nor an array."); + } + } + + /// + /// Ensure for future versions of PDFsharp not to forget code for a new kind of PdfItem. + /// + /// The item. + [Conditional("DEBUG")] + static void DebugCheckNonObjects(PdfItem item) + { + if (item is PdfName) + return; + if (item is PdfBoolean) + return; + if (item is PdfInteger) + return; + if (item is PdfNumber) + return; + if (item is PdfString) + return; + if (item is PdfRectangle) + return; + if (item is PdfNull) + return; + + Type type = item.GetType(); + Debug.Assert(type != null, string.Format("CheckNonObjects: Add {0} to the list.", type.Name)); + } + + /// + /// Gets the indirect reference of this object. If the value is null, this object is a direct object. + /// + public PdfReference Reference + { + get { return _iref; } + + // Setting the reference outside PDFsharp is not considered as a valid operation. + internal set { _iref = value; } + } + PdfReference _iref; + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfObjectID.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfObjectID.cs new file mode 100644 index 00000000..778a013b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfObjectID.cs @@ -0,0 +1,179 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a PDF object identifier, a pair of object and generation number. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public struct PdfObjectID : IComparable + { + /// + /// Initializes a new instance of the class. + /// + /// The object number. + public PdfObjectID(int objectNumber) + { + Debug.Assert(objectNumber >= 1, "Object number out of range."); + _objectNumber = objectNumber; + _generationNumber = 0; +#if DEBUG_ + // Just a place for a breakpoint during debugging. + if (objectNumber == 5894) + GetType(); +#endif + } + + /// + /// Initializes a new instance of the class. + /// + /// The object number. + /// The generation number. + public PdfObjectID(int objectNumber, int generationNumber) + { + Debug.Assert(objectNumber >= 1, "Object number out of range."); + //Debug.Assert(generationNumber >= 0 && generationNumber <= 65535, "Generation number out of range."); +#if DEBUG_ + // iText creates generation numbers with a value of 65536... + if (generationNumber > 65535) + Debug.WriteLine(String.Format("Generation number: {0}", generationNumber)); +#endif + _objectNumber = objectNumber; + _generationNumber = (ushort)generationNumber; + } + + /// + /// Gets or sets the object number. + /// + public int ObjectNumber + { + get { return _objectNumber; } + } + readonly int _objectNumber; + + /// + /// Gets or sets the generation number. + /// + public int GenerationNumber + { + get { return _generationNumber; } + } + readonly ushort _generationNumber; + + /// + /// Indicates whether this object is an empty object identifier. + /// + public bool IsEmpty + { + get { return _objectNumber == 0; } + } + + /// + /// Indicates whether this instance and a specified object are equal. + /// + public override bool Equals(object obj) + { + if (obj is PdfObjectID) + { + PdfObjectID id = (PdfObjectID)obj; + if (_objectNumber == id._objectNumber) + return _generationNumber == id._generationNumber; + } + return false; + } + + /// + /// Returns the hash code for this instance. + /// + public override int GetHashCode() + { + return _objectNumber ^ _generationNumber; + } + + /// + /// Determines whether the two objects are equal. + /// + public static bool operator ==(PdfObjectID left, PdfObjectID right) + { + return left.Equals(right); + } + + /// + /// Determines whether the tow objects not are equal. + /// + public static bool operator !=(PdfObjectID left, PdfObjectID right) + { + return !left.Equals(right); + } + + /// + /// Returns the object and generation numbers as a string. + /// + public override string ToString() + { + return _objectNumber.ToString(CultureInfo.InvariantCulture) + " " + _generationNumber.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Creates an empty object identifier. + /// + public static PdfObjectID Empty + { + get { return new PdfObjectID(); } + } + + /// + /// Compares the current object id with another object. + /// + public int CompareTo(object obj) + { + if (obj is PdfObjectID) + { + PdfObjectID id = (PdfObjectID)obj; + if (_objectNumber == id._objectNumber) + return _generationNumber - id._generationNumber; + return _objectNumber - id._objectNumber; + } + return 1; + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + internal string DebuggerDisplay + { + get { return String.Format("id=({0})", ToString()); } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfOutline.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfOutline.cs new file mode 100644 index 00000000..b0961980 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfOutline.cs @@ -0,0 +1,836 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Review: Under construction - StL/14-10-05 + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using PdfSharp.Drawing; +using PdfSharp.Pdf.Actions; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an outline item in the outlines tree. An 'outline' is also known as a 'bookmark'. + /// + public sealed class PdfOutline : PdfDictionary + { + // Reference: 8.2.2Document Outline / Page 584 + + /// + /// Initializes a new instance of the class. + /// + public PdfOutline() + { + // Create _outlines on demand. + //_outlines = new PdfOutlineCollection(this); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + internal PdfOutline(PdfDocument document) + : base(document) + { + // Create _outlines on demand. + //_outlines = new PdfOutlineCollection(this); + } + + /// + /// Initializes a new instance from an existing dictionary. Used for object type transformation. + /// + public PdfOutline(PdfDictionary dict) + : base(dict) + { + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + /// The font style used to draw the outline text. + /// The color used to draw the outline text. + public PdfOutline(string title, PdfPage destinationPage, bool opened, PdfOutlineStyle style, XColor textColor) + { + Title = title; + DestinationPage = destinationPage; + Opened = opened; + Style = style; + TextColor = textColor; + } + + /// + /// Initializes a new instance of the class. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + /// The font style used to draw the outline text. + public PdfOutline(string title, PdfPage destinationPage, bool opened, PdfOutlineStyle style) + { + Title = title; + DestinationPage = destinationPage; + Opened = opened; + Style = style; + } + + /// + /// Initializes a new instance of the class. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + public PdfOutline(string title, PdfPage destinationPage, bool opened) + { + Title = title; + DestinationPage = destinationPage; + Opened = opened; + } + + /// + /// Initializes a new instance of the class. + /// + /// The outline text. + /// The destination page. + public PdfOutline(string title, PdfPage destinationPage) + { + Title = title; + DestinationPage = destinationPage; + } + + internal int Count + { + get { return _count; } + set { _count = value; } + } + int _count; + + /// + /// The total number of open descendants at all lower levels. + /// + internal int OpenCount; + + /// + /// Counts the open outline items. Not yet used. + /// + internal int CountOpen() + { + int count = _opened ? 1 : 0; + if (_outlines != null) + count += _outlines.CountOpen(); + return count; + } + + /// + /// Gets the parent of this outline item. The root item has no parent and returns null. + /// + public PdfOutline Parent + { + get { return _parent; } + internal set { _parent = value; } + } + PdfOutline _parent; + + /// + /// Gets or sets the title. + /// + public string Title + { + get { return Elements.GetString(Keys.Title); } + set + { + PdfString s = new PdfString(value, PdfStringEncoding.Unicode); + Elements.SetValue(Keys.Title, s); + } + } + + /// + /// Gets or sets the destination page. + /// + public PdfPage DestinationPage + { + get { return _destinationPage; } + set { _destinationPage = value; } + } + PdfPage _destinationPage; + + /// + /// Gets or sets the left position of the page positioned at the left side of the window. + /// Applies only if PageDestinationType is Xyz, FitV, FitR, or FitBV. + /// + public double? Left + { + get { return _left; } + set { _left = value; } + } + double? _left = null; + + /// + /// Gets or sets the top position of the page positioned at the top side of the window. + /// Applies only if PageDestinationType is Xyz, FitH, FitR, ob FitBH. + /// + public double? Top + { + get { return _top; } + set { _top = value; } + } + double? _top = null; + + /// + /// Gets or sets the right position of the page positioned at the right side of the window. + /// Applies only if PageDestinationType is FitR. + /// + public double Right // Cannot be null in a valid PDF. + { + get { return _right; } + set { _right = value; } + } + double _right = double.NaN; + + /// + /// Gets or sets the bottom position of the page positioned at the bottom side of the window. + /// Applies only if PageDestinationType is FitR. + /// + public double Bottom // Cannot be null in a valid PDF. + { + get { return _bottom; } + set { _bottom = value; } + } + double _bottom = double.NaN; + + /// + /// Gets or sets the zoom faction of the page. + /// Applies only if PageDestinationType is Xyz. + /// + public double? Zoom + { + get { return _zoom; } + set + { + if (value.HasValue && value.Value == 0) + _zoom = null; + else + _zoom = value; + } + } + double? _zoom; // PDF treats 0 and null equally. + + /// + /// Gets or sets whether the outline item is opened (or expanded). + /// + public bool Opened + { + get { return _opened; } +#if true + set { _opened = value; } +#else + // TODO: adjust openCount of ascendant... + set + { + if (_opened != value) + { + _opened = value; + int sign = value ? 1 : -1; + PdfOutline parent = _parent; + if (_opened) + { + while (parent != null) + parent.openCount += 1 + _openCount; + } + else + { + } + } + } +#endif + } + bool _opened; + + /// + /// Gets or sets the style of the outline text. + /// + public PdfOutlineStyle Style + { + get { return (PdfOutlineStyle)Elements.GetInteger(Keys.F); } + set { Elements.SetInteger(Keys.F, (int)value); } + } + + /// + /// Gets or sets the type of the page destination. + /// + public PdfPageDestinationType PageDestinationType + { + get { return _pageDestinationType; } + set { _pageDestinationType = value; } + } + PdfPageDestinationType _pageDestinationType = PdfPageDestinationType.Xyz; + + /// + /// Gets or sets the color of the text. + /// + /// The color of the text. + public XColor TextColor + { + get { return _textColor; } + set { _textColor = value; } + } + XColor _textColor; + + /// + /// Gets a value indicating whether this outline object has child items. + /// + public bool HasChildren + { + get { return _outlines != null && _outlines.Count > 0; } + } + + /// + /// Gets the outline collection of this node. + /// + public PdfOutlineCollection Outlines + { + get { return _outlines ?? (_outlines = new PdfOutlineCollection(Owner, this)); } + } + PdfOutlineCollection _outlines; + + /// + /// Initializes this instance from an existing PDF document. + /// + void Initialize() + { + string title; + if (Elements.TryGetString(Keys.Title, out title)) + Title = title; + + PdfReference parentRef = Elements.GetReference(Keys.Parent); + if (parentRef != null) + { + PdfOutline parent = parentRef.Value as PdfOutline; + if (parent != null) + Parent = parent; + } + + Count = Elements.GetInteger(Keys.Count); + + PdfArray colors = Elements.GetArray(Keys.C); + if (colors != null && colors.Elements.Count == 3) + { + double r = colors.Elements.GetReal(0); + double g = colors.Elements.GetReal(1); + double b = colors.Elements.GetReal(2); + TextColor = XColor.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255)); + } + + // Style directly works on dictionary element. + + PdfItem dest = Elements.GetValue(Keys.Dest); + PdfItem a = Elements.GetValue(Keys.A); + Debug.Assert(dest == null || a == null, "Either destination or goto action."); + + PdfArray destArray = null; + if (dest != null) + { + destArray = dest as PdfArray; + if (destArray != null) + { + SplitDestinationPage(destArray); + } + else + { + Debug.Assert(false, "See what to do when this happened."); + } + } + else if (a != null) + { + // The dictionary should be a GoTo action. + PdfDictionary action = a as PdfDictionary; + if (action != null && action.Elements.GetName(PdfAction.Keys.S) == "/GoTo") + { + dest = action.Elements[PdfGoToAction.Keys.D]; + destArray = dest as PdfArray; + if (destArray != null) + { + // Replace Action with /Dest entry. + Elements.Remove(Keys.A); + Elements.Add(Keys.Dest, destArray); + SplitDestinationPage(destArray); + } + else + { + throw new Exception("Destination Array expected."); + } + } + else + { + Debug.Assert(false, "See what to do when this happened."); + } + } + else + { + // Neither destination page nor GoTo action. + } + + InitializeChildren(); + } + + void SplitDestinationPage(PdfArray destination) // Reference: 8.2 Destination syntax / Page 582 + { + // ReSharper disable HeuristicUnreachableCode +#pragma warning disable 162 + + // The destination page may not yet have been transformed to PdfPage. + PdfDictionary destPage = (PdfDictionary)((PdfReference)destination.Elements[0]).Value; + PdfPage page = destPage as PdfPage; + if (page == null) + page = new PdfPage(destPage); + + DestinationPage = page; + PdfName type = destination.Elements[1] as PdfName; + if (type != null) + { + PageDestinationType = (PdfPageDestinationType)Enum.Parse(typeof(PdfPageDestinationType), type.Value.Substring(1), true); + switch (PageDestinationType) + { + // [page /XYZ left top zoom] -- left, top, and zoom can be null. + case PdfPageDestinationType.Xyz: + Left = destination.Elements.GetNullableReal(2); + Top = destination.Elements.GetNullableReal(3); + Zoom = destination.Elements.GetNullableReal(4); // For this parameter, null and 0 have the same meaning. + break; + + // [page /Fit] + case PdfPageDestinationType.Fit: + // /Fit has no parameters. + break; + + // [page /FitH top] -- top can be null. + case PdfPageDestinationType.FitH: + Top = destination.Elements.GetNullableReal(2); + break; + + // [page /FitV left] -- left can be null. + case PdfPageDestinationType.FitV: + Left = destination.Elements.GetNullableReal(2); + break; + + // [page /FitR left bottom right top] -- left, bottom, right, and top must not be null. + // TODO An exception in GetReal leads to an inconsistent document. Deal with that - e.g. by registering the corruption and preventing the user from saving the corrupted document. + case PdfPageDestinationType.FitR: + Left = destination.Elements.GetReal(2); + Bottom = destination.Elements.GetReal(3); + Right = destination.Elements.GetReal(4); + Top = destination.Elements.GetReal(5); + break; + + // [page /FitB] + case PdfPageDestinationType.FitB: + // /Fit has no parameters. + break; + + // [page /FitBH top] -- top can be null. + case PdfPageDestinationType.FitBH: + Top = destination.Elements.GetReal(2); + break; + + // [page /FitBV left] -- left can be null. + case PdfPageDestinationType.FitBV: + Left = destination.Elements.GetReal(2); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + +#pragma warning restore 162 + // ReSharper restore HeuristicUnreachableCode + } + + void InitializeChildren() + { + PdfReference firstRef = Elements.GetReference(Keys.First); + PdfReference lastRef = Elements.GetReference(Keys.Last); + PdfReference current = firstRef; + while (current != null) + { + // Create item and add it to outline items dictionary. + PdfOutline item = new PdfOutline((PdfDictionary)current.Value); + Outlines.Add(item); + + current = item.Elements.GetReference(Keys.Next); +#if DEBUG_ + if (current == null) + { + if (item.Reference != lastRef) + { + // Word produces PDFs that come to this case. + GetType(); + } + } +#endif + } + } + + /// + /// Creates key/values pairs according to the object structure. + /// + internal override void PrepareForSave() + { + bool hasKids = HasChildren; + // Is something to do at all? + if (_parent != null || hasKids) + { + if (_parent == null) + { + // Case: This is the outline dictionary (the root). + // Reference: TABLE 8.3 Entries in the outline dictionary / Page 585 + Debug.Assert(_outlines != null && _outlines.Count > 0 && _outlines[0] != null); + Elements[Keys.First] = _outlines[0].Reference; + Elements[Keys.Last] = _outlines[_outlines.Count - 1].Reference; + + // TODO: /Count - the meaning is not completely clear to me. + // Get PDFs created with Acrobat and analyze what to implement. + if (OpenCount > 0) + Elements[Keys.Count] = new PdfInteger(OpenCount); + } + else + { + // Case: This is an outline item dictionary. + // Reference: TABLE 8.4 Entries in the outline item dictionary / Page 585 + Elements[Keys.Parent] = _parent.Reference; + + int count = _parent._outlines.Count; + int index = _parent._outlines.IndexOf(this); + Debug.Assert(index != -1); + + // Has destination? + if (DestinationPage != null) + Elements[Keys.Dest] = CreateDestArray(); + + // Not the first element? + if (index > 0) + Elements[Keys.Prev] = _parent._outlines[index - 1].Reference; + + // Not the last element? + if (index < count - 1) + Elements[Keys.Next] = _parent._outlines[index + 1].Reference; + + if (hasKids) + { + Elements[Keys.First] = _outlines[0].Reference; + Elements[Keys.Last] = _outlines[_outlines.Count - 1].Reference; + } + // TODO: /Count - the meaning is not completely clear to me + if (OpenCount > 0) + Elements[Keys.Count] = new PdfInteger((_opened ? 1 : -1) * OpenCount); + + if (_textColor != XColor.Empty && Owner.HasVersion("1.4")) + Elements[Keys.C] = new PdfLiteral("[{0}]", PdfEncoders.ToString(_textColor, PdfColorMode.Rgb)); + + // if (Style != PdfOutlineStyle.Regular && Document.HasVersion("1.4")) + // //pdf.AppendFormat("/F {0}\n", (int)_style); + // Elements[Keys.F] = new PdfInteger((int)_style); + } + + // Prepare child elements. + if (hasKids) + { + foreach (PdfOutline outline in _outlines) + outline.PrepareForSave(); + } + } + } + + PdfArray CreateDestArray() + { + PdfArray dest = null; + switch (PageDestinationType) + { + // [page /XYZ left top zoom] + case PdfPageDestinationType.Xyz: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/XYZ {0} {1} {2}", Fd(Left), Fd(Top), Fd(Zoom)))); + break; + + // [page /Fit] + case PdfPageDestinationType.Fit: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral("/Fit")); + break; + + // [page /FitH top] + case PdfPageDestinationType.FitH: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/FitH {0}", Fd(Top)))); + break; + + // [page /FitV left] + case PdfPageDestinationType.FitV: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/FitV {0}", Fd(Left)))); + break; + + // [page /FitR left bottom right top] + case PdfPageDestinationType.FitR: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/FitR {0} {1} {2} {3}", Fd(Left), Fd(Bottom), Fd(Right), Fd(Top)))); + break; + + // [page /FitB] + case PdfPageDestinationType.FitB: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral("/FitB")); + break; + + // [page /FitBH top] + case PdfPageDestinationType.FitBH: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/FitBH {0}", Fd(Top)))); + break; + + // [page /FitBV left] + case PdfPageDestinationType.FitBV: + dest = new PdfArray(Owner, + DestinationPage.Reference, new PdfLiteral(String.Format("/FitBV {0}", Fd(Left)))); + break; + + default: + throw new ArgumentOutOfRangeException(); + } + return dest; + } + + /// + /// Format double. + /// + string Fd(double value) + { + if (Double.IsNaN(value)) + throw new InvalidOperationException("Value is not a valid Double."); + return value.ToString("#.##", CultureInfo.InvariantCulture); + + //return Double.IsNaN(value) ? "null" : value.ToString("#.##", CultureInfo.InvariantCulture); + } + + /// + /// Format nullable double. + /// + string Fd(double? value) + { + return value.HasValue ? value.Value.ToString("#.##", CultureInfo.InvariantCulture) : "null"; + } + + internal override void WriteObject(PdfWriter writer) + { +#if DEBUG + writer.WriteRaw("% Title = " + FilterUnicode(Title) + "\n"); +#endif + // TODO: Proof that there is nothing to do here. + bool hasKids = HasChildren; + if (_parent != null || hasKids) + { + ////// Everything done in PrepareForSave + ////if (_parent == null) + ////{ + //// // This is the outline dictionary (the root) + ////} + ////else + ////{ + //// // This is an outline item dictionary + ////} + base.WriteObject(writer); + } + } + +#if DEBUG + private string FilterUnicode(string text) + { + StringBuilder result = new StringBuilder(); + foreach (char ch in text) + result.Append((uint)ch < 256 ? (ch != '\r' && ch != '\n' ? ch : ' ') : '?'); + return result.ToString(); + } +#endif + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + // ReSharper disable InconsistentNaming + + /// + /// (Optional) The type of PDF object that this dictionary describes; if present, + /// must be Outlines for an outline dictionary. + /// + [KeyInfo(KeyType.Name | KeyType.Optional, FixedValue = "Outlines")] + public const string Type = "/Type"; + + // Outline and outline item are combined + ///// + ///// (Required if there are any open or closed outline entries; must be an indirect reference) + ///// An outline item dictionary representing the first top-level item in the outline. + ///// + //[KeyInfo(KeyType.Dictionary)] + //public const string First = "/First"; + // + ///// + ///// (Required if there are any open or closed outline entries; must be an indirect reference) + ///// An outline item dictionary representing the last top-level item in the outline. + ///// + //[KeyInfo(KeyType.Dictionary)] + //public const string Last = "/Last"; + // + ///// + ///// (Required if the document has any open outline entries) The total number of open items at all + ///// levels of the outline. This entry should be omitted if there are no open outline items. + ///// + //[KeyInfo(KeyType.Integer)] + //public const string Count = "/Count"; + + /// + /// (Required) The text to be displayed on the screen for this item. + /// + [KeyInfo(KeyType.String | KeyType.Required)] + public const string Title = "/Title"; + + /// + /// (Required; must be an indirect reference) The parent of this item in the outline hierarchy. + /// The parent of a top-level item is the outline dictionary itself. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Parent = "/Parent"; + + /// + /// (Required for all but the first item at each level; must be an indirect reference) + /// The previous item at this outline level. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Prev = "/Prev"; + + /// + /// (Required for all but the last item at each level; must be an indirect reference) + /// The next item at this outline level. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Next = "/Next"; + + /// + /// (Required if the item has any descendants; must be an indirect reference) + /// The first of this items immediate children in the outline hierarchy. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string First = "/First"; + + /// + /// (Required if the item has any descendants; must be an indirect reference) + /// The last of this items immediate children in the outline hierarchy. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Last = "/Last"; + + /// + /// (Required if the item has any descendants) If the item is open, the total number of its + /// open descendants at all lower levels of the outline hierarchy. If the item is closed, a + /// negative integer whose absolute value specifies how many descendants would appear if the + /// item were reopened. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Count = "/Count"; + + /// + /// (Optional; not permitted if an A entry is present) The destination to be displayed when this + /// item is activated. + /// + [KeyInfo(KeyType.ArrayOrNameOrString | KeyType.Optional)] + public const string Dest = "/Dest"; + + /// + /// (Optional; not permitted if a Dest entry is present) The action to be performed when + /// this item is activated. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string A = "/A"; + + /// + /// (Optional; PDF 1.3; must be an indirect reference) The structure element to which the item + /// refers. + /// Note: The ability to associate an outline item with a structure element (such as the beginning + /// of a chapter) is a PDF 1.3 feature. For backward compatibility with earlier PDF versions, such + /// an item should also specify a destination (Dest) corresponding to an area of a page where the + /// contents of the designated structure element are displayed. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Optional)] + public const string SE = "/SE"; + + /// + /// (Optional; PDF 1.4) An array of three numbers in the range 0.0 to 1.0, representing the + /// components in the DeviceRGB color space of the color to be used for the outline entrys text. + /// Default value: [0.0 0.0 0.0]. + /// + [KeyInfo(KeyType.Array | KeyType.Optional)] + public const string C = "/C"; + + /// + /// (Optional; PDF 1.4) A set of flags specifying style characteristics for displaying the outline + /// items text. Default value: 0. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string F = "/F"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + + // ReSharper restore InconsistentNaming + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfOutlineCollection.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfOutlineCollection.cs new file mode 100644 index 00000000..99b7ca6d --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfOutlineCollection.cs @@ -0,0 +1,338 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Collections; +using PdfSharp.Drawing; + +// Review: CountOpen does not work. - StL/14-10-05 + +namespace PdfSharp.Pdf +{ + /// + /// Represents a collection of outlines. + /// + public class PdfOutlineCollection : PdfObject, ICollection, IList + { + /// + /// Can only be created as part of PdfOutline. + /// + internal PdfOutlineCollection(PdfDocument document, PdfOutline parent) + : base(document) + { + _parent = parent; + } + + /// + /// Indicates whether the outline collection has at least one entry. + /// + [Obsolete("Use 'Count > 0' - HasOutline will throw exception.")] + public bool HasOutline // DELETE: 15-10-01 + { + get + { + //return Count > 0; + throw new InvalidOperationException("Use 'Count > 0'"); + } + } + + /// + /// Removes the first occurrence of a specific item from the collection. + /// + public bool Remove(PdfOutline item) + { + if (_outlines.Remove(item)) + { + RemoveFromOutlinesTree(item); + return true; + } + return false; + } + + /// + /// Gets the number of entries in this collection. + /// + public int Count + { + get { return _outlines.Count; } + } + + /// + /// Returns false. + /// + public bool IsReadOnly + { + get { return false; } + } + + /// + /// Adds the specified outline. + /// + public void Add(PdfOutline outline) + { + if (outline == null) + throw new ArgumentNullException("outline"); + + // DestinationPage is optional. PDFsharp does not yet support outlines with action ("/A") instead of destination page ("/DEST") + if (outline.DestinationPage != null && !ReferenceEquals(Owner, outline.DestinationPage.Owner)) + throw new ArgumentException("Destination page must belong to this document."); + + //// TODO check the parent problems... + ////outline.Document = Owner; + ////outline.Parent = _parent; + ////Owner._irefTable.Add(outline); + + AddToOutlinesTree(outline); + _outlines.Add(outline); + + if (outline.Opened) + { + outline = _parent; + while (outline != null) + { + outline.OpenCount++; + outline = outline.Parent; + } + } + } + + /// + /// Removes all elements form the collection. + /// + public void Clear() + { + if (Count > 0) + { + PdfOutline[] array = new PdfOutline[Count]; + _outlines.CopyTo(array); + _outlines.Clear(); + foreach (PdfOutline item in array) + { + RemoveFromOutlinesTree(item); + } + } + } + + /// + /// Determines whether the specified element is in the collection. + /// + public bool Contains(PdfOutline item) + { + return _outlines.Contains(item); + } + + /// + /// Copies the collection to an array, starting at the specified index of the target array. + /// + public void CopyTo(PdfOutline[] array, int arrayIndex) + { + _outlines.CopyTo(array, arrayIndex); + } + + /// + /// Adds the specified outline entry. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + /// The font style used to draw the outline text. + /// The color used to draw the outline text. + public PdfOutline Add(string title, PdfPage destinationPage, bool opened, PdfOutlineStyle style, XColor textColor) + { + PdfOutline outline = new PdfOutline(title, destinationPage, opened, style, textColor); + Add(outline); + return outline; + } + + /// + /// Adds the specified outline entry. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + /// The font style used to draw the outline text. + public PdfOutline Add(string title, PdfPage destinationPage, bool opened, PdfOutlineStyle style) + { + PdfOutline outline = new PdfOutline(title, destinationPage, opened, style); + Add(outline); + return outline; + } + + /// + /// Adds the specified outline entry. + /// + /// The outline text. + /// The destination page. + /// Specifies whether the node is displayed expanded (opened) or collapsed. + public PdfOutline Add(string title, PdfPage destinationPage, bool opened) + { + PdfOutline outline = new PdfOutline(title, destinationPage, opened); + Add(outline); + return outline; + } + + /// + /// Creates a PdfOutline and adds it into the outline collection. + /// + public PdfOutline Add(string title, PdfPage destinationPage) + { + PdfOutline outline = new PdfOutline(title, destinationPage); + Add(outline); + return outline; + } + + /// + /// Gets the index of the specified item. + /// + public int IndexOf(PdfOutline item) + { + return _outlines.IndexOf(item); + } + + /// + /// Inserts the item at the specified index. + /// + public void Insert(int index, PdfOutline outline) + { + if (outline == null) + throw new ArgumentNullException("outline"); + if (index < 0 || index >= _outlines.Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.OutlineIndexOutOfRange); + + AddToOutlinesTree(outline); + _outlines.Insert(index, outline); + } + + /// + /// Removes the outline item at the specified index. + /// + public void RemoveAt(int index) + { + PdfOutline outline = _outlines[index]; + _outlines.RemoveAt(index); + RemoveFromOutlinesTree(outline); + } + + /// + /// Gets the at the specified index. + /// + public PdfOutline this[int index] + { + get + { + if (index < 0 || index >= _outlines.Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.OutlineIndexOutOfRange); + return _outlines[index]; + } + set + { + if (index < 0 || index >= _outlines.Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.OutlineIndexOutOfRange); + if (value == null) + throw new ArgumentOutOfRangeException("value", null, PSSR.SetValueMustNotBeNull); + + AddToOutlinesTree(value); + _outlines[index] = value; + } + } + + /// + /// Returns an enumerator that iterates through the outline collection. + /// + public IEnumerator GetEnumerator() + { + return _outlines.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal int CountOpen() + { + int count = 0; + //foreach (PdfOutline outline in _outlines) + // count += outline.CountOpen(); + return count; + } + + void AddToOutlinesTree(PdfOutline outline) + { + if (outline == null) + throw new ArgumentNullException("outline"); + + // DestinationPage is optional. PDFsharp does not yet support outlines with action ("/A") instead of destination page ("/DEST") + if (outline.DestinationPage != null && !ReferenceEquals(Owner, outline.DestinationPage.Owner)) + throw new ArgumentException("Destination page must belong to this document."); + + // TODO check the parent problems... + outline.Document = Owner; + outline.Parent = _parent; + + //_outlines.Add(outline); + if (!Owner._irefTable.Contains(outline.ObjectID)) + Owner._irefTable.Add(outline); + else + { + outline.GetType(); + } + + //if (outline.Opened) + //{ + // outline = _parent; + // while (outline != null) + // { + // outline.OpenCount++; + // outline = outline.Parent; + // } + //} + } + + void RemoveFromOutlinesTree(PdfOutline outline) + { + if (outline == null) + throw new ArgumentNullException("outline"); + + // TODO check the parent problems... + //outline.Document = Owner; + outline.Parent = null; + + Owner._irefTable.Remove(outline.Reference); + } + + /// + /// The parent outine of this collection. + /// + readonly PdfOutline _parent; + + readonly List _outlines = new List(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfPage.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfPage.cs new file mode 100644 index 00000000..cfe7f484 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfPage.cs @@ -0,0 +1,1089 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using System.ComponentModel; +using PdfSharp.Pdf.IO; +using PdfSharp.Drawing; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Annotations; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a page in a PDF document. + /// + public sealed class PdfPage : PdfDictionary, IContentStream + { + /// + /// Initializes a new page. The page must be added to a document before it can be used. + /// Depending of the IsMetric property of the current region the page size is set to + /// A4 or Letter respectively. If this size is not appropriate it should be changed before + /// any drawing operations are performed on the page. + /// + public PdfPage() + { + Elements.SetName(Keys.Type, "/Page"); + Initialize(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + public PdfPage(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Page"); + Elements[Keys.Parent] = document.Pages.Reference; + Initialize(); + } + + internal PdfPage(PdfDictionary dict) + : base(dict) + { + // Set Orientation depending on /Rotate. + + //!!!modTHHO 2016-06-16 Do not set Orientation here. Setting Orientation is not enough. Other properties must also be changed when setting Orientation. + //!!!modTHHO 2018-04-05 Restored the old behavior. Commenting the next three lines out is not enough either. + // New approach: remember that Orientation was set based on rotation. + int rotate = Elements.GetInteger(InheritablePageKeys.Rotate); + if (Math.Abs((rotate / 90)) % 2 == 1) + { +#if true + _orientation = PageOrientation.Landscape; + // Hacky approach: do not swap width and height on saving when orientation was set here. + _orientationSetByCodeForRotatedDocument = true; +#else + // Cleaner approach: Swap width and height here. But some drawing routines will not draw the XPdfForm correctly, so this needs more testing and more changes. + // When saving, width and height will be swapped. So we have to swap them here too. + PdfRectangle mediaBox = MediaBox; + MediaBox = new PdfRectangle(mediaBox.X1, mediaBox.Y1, mediaBox.Y2, mediaBox.X2); +#endif + } + } + + void Initialize() + { + Size = RegionInfo.CurrentRegion.IsMetric ? PageSize.A4 : PageSize.Letter; + +#pragma warning disable 168 + // Force creation of MediaBox object by invoking property. + PdfRectangle rect = MediaBox; +#pragma warning restore 168 + } + + /// + /// Gets or sets a user defined object that contains arbitrary information associated with this PDF page. + /// The tag is not used by PDFsharp. + /// + public object Tag + { + get { return _tag; } + set { _tag = value; } + } + object _tag; + + /// + /// Closes the page. A closed page cannot be modified anymore and it is not possible to + /// get an XGraphics object for a closed page. Closing a page is not required, but may save + /// resources if the document has many pages. + /// + public void Close() + { + //// Close renderer, if any + //if (_content.pdfRenderer != null) + // _content.pdfRenderer.endp.Close(); + _closed = true; + } + bool _closed; + + /// + /// Gets a value indicating whether the page is closed. + /// + internal bool IsClosed + { + get { return _closed; } + } + + /// + /// Gets or sets the PdfDocument this page belongs to. + /// + internal override PdfDocument Document + { + set + { + if (!ReferenceEquals(_document, value)) + { + if (_document != null) + throw new InvalidOperationException("Cannot change document."); + _document = value; + if (Reference != null) + Reference.Document = value; + Elements[Keys.Parent] = _document.Pages.Reference; + } + } + } + + /// + /// Gets or sets the orientation of the page. The default value PageOrientation.Portrait. + /// If an imported page has a /Rotate value that matches the formula 90 + n * 180 the + /// orientation is set to PageOrientation.Landscape. + /// + public PageOrientation Orientation + { + get { return _orientation; } + set + { + _orientation = value; + _orientationSetByCodeForRotatedDocument = false; + } + } + PageOrientation _orientation; + bool _orientationSetByCodeForRotatedDocument; + // TODO Simplify the implementation. Should /Rotate 90 lead to Landscape format? + // TODO Clean implementation without _orientationSetByCodeForRotatedDocument. + + /// + /// Gets or sets one of the predefined standard sizes like. + /// + public PageSize Size + { + get { return _pageSize; } + set + { + if (!Enum.IsDefined(typeof(PageSize), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(PageSize)); + + XSize size = PageSizeConverter.ToSize(value); + // MediaBox is always in Portrait mode (see Height, Width). + // So take Orientation NOT into account. + MediaBox = new PdfRectangle(0, 0, size.Width, size.Height); + _pageSize = value; + } + } + PageSize _pageSize; + + /// + /// Gets or sets the trim margins. + /// + public TrimMargins TrimMargins + { + get + { + if (_trimMargins == null) + _trimMargins = new TrimMargins(); + return _trimMargins; + } + set + { + if (_trimMargins == null) + _trimMargins = new TrimMargins(); + if (value != null) + { + _trimMargins.Left = value.Left; + _trimMargins.Right = value.Right; + _trimMargins.Top = value.Top; + _trimMargins.Bottom = value.Bottom; + } + else + _trimMargins.All = 0; + } + } + TrimMargins _trimMargins = new TrimMargins(); + + /// + /// Gets or sets the media box directly. XGrahics is not prepared to work with a media box + /// with an origin other than (0,0). + /// + public PdfRectangle MediaBox + { + get { return Elements.GetRectangle(Keys.MediaBox, true); } + set { Elements.SetRectangle(Keys.MediaBox, value); } + } + + /// + /// Gets or sets the crop box. + /// + public PdfRectangle CropBox + { + get { return Elements.GetRectangle(Keys.CropBox, true); } + set { Elements.SetRectangle(Keys.CropBox, value); } + } + + /// + /// Gets or sets the bleed box. + /// + public PdfRectangle BleedBox + { + get { return Elements.GetRectangle(Keys.BleedBox, true); } + set { Elements.SetRectangle(Keys.BleedBox, value); } + } + + /// + /// Gets or sets the art box. + /// + public PdfRectangle ArtBox + { + get { return Elements.GetRectangle(Keys.ArtBox, true); } + set { Elements.SetRectangle(Keys.ArtBox, value); } + } + + /// + /// Gets or sets the trim box. + /// + public PdfRectangle TrimBox + { + get { return Elements.GetRectangle(Keys.TrimBox, true); } + set { Elements.SetRectangle(Keys.TrimBox, value); } + } + + /// + /// Gets or sets the height of the page. If orientation is Landscape, this function applies to + /// the width. + /// + public XUnit Height + { + get + { + PdfRectangle rect = MediaBox; + return _orientation == PageOrientation.Portrait ? rect.Height : rect.Width; + } + set + { + PdfRectangle rect = MediaBox; + if (_orientation == PageOrientation.Portrait) + MediaBox = new PdfRectangle(rect.X1, 0, rect.X2, value); + else + MediaBox = new PdfRectangle(0, rect.Y1, value, rect.Y2); + _pageSize = PageSize.Undefined; + } + } + + /// + /// Gets or sets the width of the page. If orientation is Landscape, this function applies to + /// the height. + /// + public XUnit Width + { + get + { + PdfRectangle rect = MediaBox; + return _orientation == PageOrientation.Portrait ? rect.Width : rect.Height; + } + set + { + PdfRectangle rect = MediaBox; + if (_orientation == PageOrientation.Portrait) + MediaBox = new PdfRectangle(0, rect.Y1, value, rect.Y2); + else + MediaBox = new PdfRectangle(rect.X1, 0, rect.X2, value); + _pageSize = PageSize.Undefined; + } + } + + /// + /// Gets or sets the /Rotate entry of the PDF page. The value is the number of degrees by which the page + /// should be rotated clockwise when displayed or printed. The value must be a multiple of 90. + /// PDFsharp does not set this value, but for imported pages this value can be set and must be taken + /// into account when adding graphic to such a page. + /// + public int Rotate + { + get { return _elements.GetInteger(InheritablePageKeys.Rotate); } + set + { + if (value % 90 != 0) + throw new ArgumentException("Value must be a multiple of 90."); + _elements.SetInteger(InheritablePageKeys.Rotate, value); + } + } + + // TODO: PdfAnnotations + // TODO: PdfActions + // TODO: PdfPageTransition + + /// + /// The content stream currently used by an XGraphics object for rendering. + /// + internal PdfContent RenderContent; + + /// + /// Gets the array of content streams of the page. + /// + public PdfContents Contents + { + get + { + if (_contents == null) + { + if (true) // || Document.IsImported) + { + PdfItem item = Elements[Keys.Contents]; + if (item == null) + { + _contents = new PdfContents(Owner); + //Owner.irefTable.Add(_contents); + } + else + { + if (item is PdfReference) + item = ((PdfReference)item).Value; + + PdfArray array = item as PdfArray; + if (array != null) + { + // It is already an array of content streams. + if (array.IsIndirect) + { + // Make it a direct array + array = array.Clone(); + array.Document = Owner; + } + // TODO 4STLA: Causes Exception "Object type transformation must not be done with direct objects" in "protected PdfObject(PdfObject obj)" + _contents = new PdfContents(array); + } + else + { + // Only one content stream -> create array + _contents = new PdfContents(Owner); + //Owner.irefTable.Add(_contents); + PdfContent content = new PdfContent((PdfDictionary)item); + _contents.Elements.Add(content.Reference); + } + } + } + //else + //{ + // _content = new PdfContent(Document); + // Document.xrefTable.Add(_content); + //} + Debug.Assert(_contents.Reference == null); + Elements[Keys.Contents] = _contents; + } + return _contents; + } + } + PdfContents _contents; + +#region Annotations + + /// + /// Gets the annotations array of this page. + /// + public bool HasAnnotations + { + get + { + if (_annotations == null) + { + // Get annotations array if exists. + _annotations = (PdfAnnotations)Elements.GetValue(Keys.Annots); + _annotations.Page = this; + } + return _annotations != null; + } + } + + /// + /// Gets the annotations array of this page. + /// + public PdfAnnotations Annotations + { + get + { + if (_annotations == null) + { + // Get or create annotations array. + _annotations = (PdfAnnotations)Elements.GetValue(Keys.Annots, VCF.Create); + _annotations.Page = this; + } + return _annotations; + } + } + PdfAnnotations _annotations; + + /// + /// Adds an internal document link. + /// + /// The link area in default page coordinates. + /// The destination page. + public PdfLinkAnnotation AddDocumentLink(PdfRectangle rect, int destinationPage) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateDocumentLink(rect, destinationPage); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds an internal document link. + /// + /// The link area in default page coordinates. + /// The Named Destination's name. + public PdfLinkAnnotation AddDocumentLink(PdfRectangle rect, string destinationName) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateDocumentLink(rect, destinationName); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds an external document link. + /// + /// The link area in default page coordinates. + /// The path to the target document. + /// The Named Destination's name in the target document. + /// True, if the destination document shall be opened in a new window. If not set, the viewer application should behave in accordance with the current user preference. + public PdfLinkAnnotation AddDocumentLink(PdfRectangle rect, string documentPath, string destinationName, bool? newWindow = null) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateDocumentLink(rect, documentPath, destinationName, newWindow); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds an embedded document link. + /// + /// The link area in default page coordinates. + /// The path to the named destination through the embedded documents. + /// The path is separated by '\' and the last segment is the name of the named destination. + /// The other segments describe the route from the current (root or embedded) document to the embedded document holding the destination. + /// ".." references to the parent, other strings refer to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public PdfLinkAnnotation AddEmbeddedDocumentLink(PdfRectangle rect, string destinationPath, bool? newWindow = null) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateEmbeddedDocumentLink(rect, destinationPath, newWindow); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds an external embedded document link. + /// + /// The link area in default page coordinates. + /// The path to the target document. + /// The path to the named destination through the embedded documents in the target document. + /// The path is separated by '\' and the last segment is the name of the named destination. + /// The other segments describe the route from the root document to the embedded document. + /// Each segment name refers to a child with this name in the EmbeddedFiles name dictionary. + /// True, if the destination document shall be opened in a new window. + /// If not set, the viewer application should behave in accordance with the current user preference. + public PdfLinkAnnotation AddEmbeddedDocumentLink(PdfRectangle rect, string documentPath, string destinationPath, bool? newWindow = null) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateEmbeddedDocumentLink(rect, documentPath, destinationPath, newWindow); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds a link to the Web. + /// + /// The rect. + /// The URL. + public PdfLinkAnnotation AddWebLink(PdfRectangle rect, string url) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateWebLink(rect, url); + Annotations.Add(annotation); + return annotation; + } + + /// + /// Adds a link to a file. + /// + /// The rect. + /// Name of the file. + public PdfLinkAnnotation AddFileLink(PdfRectangle rect, string fileName) + { + PdfLinkAnnotation annotation = PdfLinkAnnotation.CreateFileLink(rect, fileName); + Annotations.Add(annotation); + return annotation; + } + +#endregion + + /// + /// Gets or sets the custom values. + /// + public PdfCustomValues CustomValues + { + get + { + if (_customValues == null) + _customValues = PdfCustomValues.Get(Elements); + return _customValues; + } + set + { + if (value != null) + throw new ArgumentException("Only null is allowed to clear all custom values."); + PdfCustomValues.Remove(Elements); + _customValues = null; + } + } + PdfCustomValues _customValues; + + /// + /// Gets the PdfResources object of this page. + /// + public PdfResources Resources + { + get + { + if (_resources == null) + _resources = (PdfResources)Elements.GetValue(Keys.Resources, VCF.Create); //VCF.CreateIndirect + return _resources; + } + } + PdfResources _resources; + + /// + /// Implements the interface because the primary function is internal. + /// + PdfResources IContentStream.Resources + { + get { return Resources; } + } + + /// + /// Gets the resource name of the specified font within this page. + /// + internal string GetFontName(XFont font, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(font); + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(XFont font, out PdfFont pdfFont) + { + return GetFontName(font, out pdfFont); + } + + /// + /// Tries to get the resource name of the specified font data within this page. + /// Returns null if no such font exists. + /// + internal string TryGetFontName(string idName, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.TryGetFont(idName); + string name = null; + if (pdfFont != null) + name = Resources.AddFont(pdfFont); + return name; + } + + /// + /// Gets the resource name of the specified font data within this page. + /// + internal string GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + pdfFont = _document.FontTable.GetFont(idName, fontData); + //pdfFont = new PdfType0Font(Owner, idName, fontData); + //pdfFont.Document = _document; + Debug.Assert(pdfFont != null); + string name = Resources.AddFont(pdfFont); + return name; + } + + string IContentStream.GetFontName(string idName, byte[] fontData, out PdfFont pdfFont) + { + return GetFontName(idName, fontData, out pdfFont); + } + + /// + /// Gets the resource name of the specified image within this page. + /// + internal string GetImageName(XImage image) + { + PdfImage pdfImage = _document.ImageTable.GetImage(image); + Debug.Assert(pdfImage != null); + string name = Resources.AddImage(pdfImage); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetImageName(XImage image) + { + return GetImageName(image); + } + + /// + /// Gets the resource name of the specified form within this page. + /// + internal string GetFormName(XForm form) + { + PdfFormXObject pdfForm = _document.FormTable.GetForm(form); + Debug.Assert(pdfForm != null); + string name = Resources.AddForm(pdfForm); + return name; + } + + /// + /// Implements the interface because the primary function is internal. + /// + string IContentStream.GetFormName(XForm form) + { + return GetFormName(form); + } + + internal override void WriteObject(PdfWriter writer) + { + // HACK: temporarily flip media box if Landscape + PdfRectangle mediaBox = MediaBox; + // TODO: Take /Rotate into account + //!!!newTHHO 2018-04-05 Stop manipulating the MediaBox - Height and Width properties already take orientation into account. + //!!!delTHHO 2018-04-05 if (_orientation == PageOrientation.Landscape) + //!!!delTHHO 2018-04-05 MediaBox = new PdfRectangle(mediaBox.X1, mediaBox.Y1, mediaBox.Y2, mediaBox.X2); + // One step back - swap members in MediaBox for landscape orientation. + if (_orientation == PageOrientation.Landscape && !_orientationSetByCodeForRotatedDocument) + MediaBox = new PdfRectangle(mediaBox.X1, mediaBox.Y1, mediaBox.Y2, mediaBox.X2); + +#if true + // Add transparency group to prevent rendering problems of Adobe viewer. + // Update (PDFsharp 1.50 beta 3): Add transparency group only if ColorMode is defined. + // Rgb is the default for the ColorMode, but if user sets it to Undefined then + // we respect this and skip the transparency group. + TransparencyUsed = true; // TODO: check XObjects + if (TransparencyUsed && !Elements.ContainsKey(Keys.Group) && + _document.Options.ColorMode != PdfColorMode.Undefined) + { + PdfDictionary group = new PdfDictionary(); + _elements["/Group"] = group; + if (_document.Options.ColorMode != PdfColorMode.Cmyk) + group.Elements.SetName("/CS", "/DeviceRGB"); + else + group.Elements.SetName("/CS", "/DeviceCMYK"); + group.Elements.SetName("/S", "/Transparency"); + //False is default: group.Elements["/I"] = new PdfBoolean(false); + //False is default: group.Elements["/K"] = new PdfBoolean(false); + } +#endif + +#if DEBUG_ + PdfItem item = Elements["/MediaBox"]; + if (item != null) + item.GetType(); +#endif + base.WriteObject(writer); + + //!!!delTHHO 2018-04-05 if (_orientation == PageOrientation.Landscape) + //!!!delTHHO 2018-04-05 MediaBox = mediaBox; + // One step back - swap members in MediaBox for landscape orientation. + if (_orientation == PageOrientation.Landscape && !_orientationSetByCodeForRotatedDocument) + MediaBox = mediaBox; + } + + /// + /// Hack to indicate that a page-level transparency group must be created. + /// + internal bool TransparencyUsed; + + /// + /// Inherit values from parent node. + /// + internal static void InheritValues(PdfDictionary page, InheritedValues values) + { + // HACK: I'M ABSOLUTELY NOT SURE WHETHER THIS CODE COVERS ALL CASES. + if (values.Resources != null) + { + PdfDictionary resources; + PdfItem res = page.Elements[InheritablePageKeys.Resources]; + if (res is PdfReference) + { + resources = (PdfDictionary)((PdfReference)res).Value.Clone(); + resources.Document = page.Owner; + } + else + resources = (PdfDictionary)res; + + if (resources == null) + { + resources = values.Resources.Clone(); + resources.Document = page.Owner; + page.Elements.Add(InheritablePageKeys.Resources, resources); + } + else + { + foreach (PdfName name in values.Resources.Elements.KeyNames) + { + if (!resources.Elements.ContainsKey(name.Value)) + { + PdfItem item = values.Resources.Elements[name]; + if (item is PdfObject) + item = item.Clone(); + resources.Elements.Add(name.ToString(), item); + } + } + } + } + + if (values.MediaBox != null && page.Elements[InheritablePageKeys.MediaBox] == null) + page.Elements[InheritablePageKeys.MediaBox] = values.MediaBox; + + if (values.CropBox != null && page.Elements[InheritablePageKeys.CropBox] == null) + page.Elements[InheritablePageKeys.CropBox] = values.CropBox; + + if (values.Rotate != null && page.Elements[InheritablePageKeys.Rotate] == null) + page.Elements[InheritablePageKeys.Rotate] = values.Rotate; + } + + /// + /// Add all inheritable values from the specified page to the specified values structure. + /// + internal static void InheritValues(PdfDictionary page, ref InheritedValues values) + { + PdfItem item = page.Elements[InheritablePageKeys.Resources]; + if (item != null) + { + PdfReference reference = item as PdfReference; + if (reference != null) + values.Resources = (PdfDictionary)(reference.Value); + else + values.Resources = (PdfDictionary)item; + } + + item = page.Elements[InheritablePageKeys.MediaBox]; + if (item != null) + values.MediaBox = new PdfRectangle(item); + + item = page.Elements[InheritablePageKeys.CropBox]; + if (item != null) + values.CropBox = new PdfRectangle(item); + + item = page.Elements[InheritablePageKeys.Rotate]; + if (item != null) + { + if (item is PdfReference) + item = ((PdfReference)item).Value; + values.Rotate = (PdfInteger)item; + } + } + + internal override void PrepareForSave() + { + if (_trimMargins.AreSet) + { + // These are the values InDesign set for an A4 page with 3mm crop margin at each edge. + // (recall that PDF rect are two points and NOT a point and a width) + // /MediaBox[0.0 0.0 612.283 858.898] 216 302.7 + // /CropBox[0.0 0.0 612.283 858.898] + // /BleedBox[0.0 0.0 612.283 858.898] + // /ArtBox[8.50394 8.50394 603.78 850.394] 3 3 213 300 + // /TrimBox[8.50394 8.50394 603.78 850.394] + + double width = _trimMargins.Left.Point + Width.Point + _trimMargins.Right.Point; + double height = _trimMargins.Top.Point + Height.Point + _trimMargins.Bottom.Point; + + MediaBox = new PdfRectangle(0, 0, width, height); + CropBox = new PdfRectangle(0, 0, width, height); + BleedBox = new PdfRectangle(0, 0, width, height); + + PdfRectangle rect = new PdfRectangle(_trimMargins.Left.Point, _trimMargins.Top.Point, + width - _trimMargins.Right.Point, height - _trimMargins.Bottom.Point); + TrimBox = rect; + ArtBox = rect.Clone(); + } + } + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : InheritablePageKeys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Page for a page object. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Page")] + public const string Type = "/Type"; + + /// + /// (Required; must be an indirect reference) + /// The page tree node that is the immediate parent of this page object. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required | KeyType.MustBeIndirect)] + public const string Parent = "/Parent"; + + /// + /// (Required if PieceInfo is present; optional otherwise; PDF 1.3) The date and time + /// when the pages contents were most recently modified. If a page-piece dictionary + /// (PieceInfo) is present, the modification date is used to ascertain which of the + /// application data dictionaries that it contains correspond to the current content + /// of the page. + /// + [KeyInfo(KeyType.Date)] + public const string LastModified = "/LastModified"; + + /// + /// (Optional; PDF 1.3) A rectangle, expressed in default user space units, defining the + /// region to which the contents of the page should be clipped when output in a production + /// environment. Default value: the value of CropBox. + /// + [KeyInfo("1.3", KeyType.Rectangle | KeyType.Optional)] + public const string BleedBox = "/BleedBox"; + + /// + /// (Optional; PDF 1.3) A rectangle, expressed in default user space units, defining the + /// intended dimensions of the finished page after trimming. Default value: the value of + /// CropBox. + /// + [KeyInfo("1.3", KeyType.Rectangle | KeyType.Optional)] + public const string TrimBox = "/TrimBox"; + + /// + /// (Optional; PDF 1.3) A rectangle, expressed in default user space units, defining the + /// extent of the pages meaningful content (including potential white space) as intended + /// by the pages creator. Default value: the value of CropBox. + /// + [KeyInfo("1.3", KeyType.Rectangle | KeyType.Optional)] + public const string ArtBox = "/ArtBox"; + + /// + /// (Optional; PDF 1.4) A box color information dictionary specifying the colors and other + /// visual characteristics to be used in displaying guidelines on the screen for the various + /// page boundaries. If this entry is absent, the application should use its own current + /// default settings. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional)] + public const string BoxColorInfo = "/BoxColorInfo"; + + /// + /// (Optional) A content stream describing the contents of this page. If this entry is absent, + /// the page is empty. The value may be either a single stream or an array of streams. If the + /// value is an array, the effect is as if all of the streams in the array were concatenated, + /// in order, to form a single stream. This allows PDF producers to create image objects and + /// other resources as they occur, even though they interrupt the content stream. The division + /// between streams may occur only at the boundaries between lexical tokens but is unrelated + /// to the pages logical content or organization. Applications that consume or produce PDF + /// files are not required to preserve the existing structure of the Contents array. + /// + [KeyInfo(KeyType.Array | KeyType.Stream | KeyType.Optional)] + public const string Contents = "/Contents"; + + /// + /// (Optional; PDF 1.4) A group attributes dictionary specifying the attributes of the pages + /// page group for use in the transparent imaging model. + /// + [KeyInfo("1.4", KeyType.Dictionary | KeyType.Optional)] + public const string Group = "/Group"; + + /// + /// (Optional) A stream object defining the pages thumbnail image. + /// + [KeyInfo(KeyType.Stream | KeyType.Optional)] + public const string Thumb = "/Thumb"; + + /// + /// (Optional; PDF 1.1; recommended if the page contains article beads) An array of indirect + /// references to article beads appearing on the page. The beads are listed in the array in + /// natural reading order. + /// + [KeyInfo("1.1", KeyType.Array | KeyType.Optional)] + public const string B = "/B"; + + /// + /// (Optional; PDF 1.1) The pages display duration (also called its advance timing): the + /// maximum length of time, in seconds, that the page is displayed during presentations before + /// the viewer application automatically advances to the next page. By default, the viewer does + /// not advance automatically. + /// + [KeyInfo("1.1", KeyType.Real | KeyType.Optional)] + public const string Dur = "/Dur"; + + /// + /// (Optional; PDF 1.1) A transition dictionary describing the transition effect to be used + /// when displaying the page during presentations. + /// + [KeyInfo("1.1", KeyType.Dictionary | KeyType.Optional)] + public const string Trans = "/Trans"; + + /// + /// (Optional) An array of annotation dictionaries representing annotations associated with + /// the page. + /// + [KeyInfo(KeyType.Array | KeyType.Optional, typeof(PdfAnnotations))] + public const string Annots = "/Annots"; + + /// + /// (Optional; PDF 1.2) An additional-actions dictionary defining actions to be performed + /// when the page is opened or closed. + /// + [KeyInfo("1.2", KeyType.Dictionary | KeyType.Optional)] + public const string AA = "/AA"; + + /// + /// (Optional; PDF 1.4) A metadata stream containing metadata for the page. + /// + [KeyInfo("1.4", KeyType.Stream | KeyType.Optional)] + public const string Metadata = "/Metadata"; + + /// + /// (Optional; PDF 1.3) A page-piece dictionary associated with the page. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional)] + public const string PieceInfo = "/PieceInfo"; + + /// + /// (Required if the page contains structural content items; PDF 1.3) + /// The integer key of the pages entry in the structural parent tree. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string StructParents = "/StructParents"; + + /// + /// (Optional; PDF 1.3; indirect reference preferred) The digital identifier of + /// the pages parent Web Capture content set. + /// + [KeyInfo("1.3", KeyType.String | KeyType.Optional)] + public const string ID = "/ID"; + + /// + /// (Optional; PDF 1.3) The pages preferred zoom (magnification) factor: the factor + /// by which it should be scaled to achieve the natural display magnification. + /// + [KeyInfo("1.3", KeyType.Real | KeyType.Optional)] + public const string PZ = "/PZ"; + + /// + /// (Optional; PDF 1.3) A separation dictionary containing information needed + /// to generate color separations for the page. + /// + [KeyInfo("1.3", KeyType.Dictionary | KeyType.Optional)] + public const string SeparationInfo = "/SeparationInfo"; + + /// + /// (Optional; PDF 1.5) A name specifying the tab order to be used for annotations + /// on the page. The possible values are R (row order), C (column order), + /// and S (structure order). + /// + [KeyInfo("1.5", KeyType.Name | KeyType.Optional)] + public const string Tabs = "/Tabs"; + + /// + /// (Required if this page was created from a named page object; PDF 1.5) + /// The name of the originating page object. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string TemplateInstantiated = "/TemplateInstantiated"; + + /// + /// (Optional; PDF 1.5) A navigation node dictionary representing the first node + /// on the page. + /// + [KeyInfo("1.5", KeyType.Dictionary | KeyType.Optional)] + public const string PresSteps = "/PresSteps"; + + /// + /// (Optional; PDF 1.6) A positive number giving the size of default user space units, + /// in multiples of 1/72 inch. The range of supported values is implementation-dependent. + /// + [KeyInfo("1.6", KeyType.Real | KeyType.Optional)] + public const string UserUnit = "/UserUnit"; + + /// + /// (Optional; PDF 1.6) An array of viewport dictionaries specifying rectangular regions + /// of the page. + /// + [KeyInfo("1.6", KeyType.Dictionary | KeyType.Optional)] + public const string VP = "/VP"; + + /// + /// Gets the KeysMeta for these keys. + /// + internal static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + + /// + /// Predefined keys common to PdfPage and PdfPages. + /// + internal class InheritablePageKeys : KeysBase + { + /// + /// (Required; inheritable) A dictionary containing any resources required by the page. + /// If the page requires no resources, the value of this entry should be an empty dictionary. + /// Omitting the entry entirely indicates that the resources are to be inherited from an + /// ancestor node in the page tree. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required | KeyType.Inheritable, typeof(PdfResources))] + public const string Resources = "/Resources"; + + /// + /// (Required; inheritable) A rectangle, expressed in default user space units, defining the + /// boundaries of the physical medium on which the page is intended to be displayed or printed. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Required | KeyType.Inheritable)] + public const string MediaBox = "/MediaBox"; + + /// + /// (Optional; inheritable) A rectangle, expressed in default user space units, defining the + /// visible region of default user space. When the page is displayed or printed, its contents + /// are to be clipped (cropped) to this rectangle and then imposed on the output medium in some + /// implementation defined manner. Default value: the value of MediaBox. + /// + [KeyInfo(KeyType.Rectangle | KeyType.Optional | KeyType.Inheritable)] + public const string CropBox = "/CropBox"; + + /// + /// (Optional; inheritable) The number of degrees by which the page should be rotated clockwise + /// when displayed or printed. The value must be a multiple of 90. Default value: 0. + /// + [KeyInfo(KeyType.Integer | KeyType.Optional)] + public const string Rotate = "/Rotate"; + } + + /// + /// Values inherited from a parent in the parent chain of a page tree. + /// + internal struct InheritedValues + { + public PdfDictionary Resources; + public PdfRectangle MediaBox; + public PdfRectangle CropBox; + public PdfInteger Rotate; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfPages.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfPages.cs new file mode 100644 index 00000000..de2ee441 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfPages.cs @@ -0,0 +1,780 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Collections; +using PdfSharp.Events; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.Annotations; + +namespace PdfSharp.Pdf +{ + /// + /// Represents the pages of the document. + /// + [DebuggerDisplay("(PageCount={Count})")] + public sealed class PdfPages : PdfDictionary, IEnumerable + { + internal PdfPages(PdfDocument document) + : base(document) + { + Elements.SetName(Keys.Type, "/Pages"); + Elements[Keys.Count] = new PdfInteger(0); + } + + internal PdfPages(PdfDictionary dictionary) + : base(dictionary) + { } + + /// + /// Gets the number of pages. + /// + public int Count + { + get { return PagesArray.Elements.Count; } + } + + /// + /// Gets the page with the specified index. + /// + public PdfPage this[int index] + { + get + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException("index", index, PSSR.PageIndexOutOfRange); + + PdfDictionary dict = (PdfDictionary)((PdfReference)PagesArray.Elements[index]).Value; + if (!(dict is PdfPage)) + dict = new PdfPage(dict); + return (PdfPage)dict; + } + } + + /// + /// Finds a page by its id. Transforms it to PdfPage if necessary. + /// + internal PdfPage FindPage(PdfObjectID id) // TODO: public? + { + PdfPage page = null; + foreach (PdfItem item in PagesArray) + { + PdfReference reference = item as PdfReference; + if (reference != null) + { + PdfDictionary dictionary = reference.Value as PdfDictionary; + if (dictionary != null && dictionary.ObjectID == id) + { + page = dictionary as PdfPage ?? new PdfPage(dictionary); + break; + } + } + } + return page; + } + + /// + /// Creates a new PdfPage, adds it to the end of this document, and returns it. + /// + public PdfPage Add() + { + PdfPage page = new PdfPage(); + Insert(Count, page); + return page; + } + + /// + /// Adds the specified PdfPage to the end of this document and maybe returns a new PdfPage object. + /// The value returned is a new object if the added page comes from a foreign document. + /// + public PdfPage Add(PdfPage page) + { + return Insert(Count, page); + } + + /// + /// Creates a new PdfPage, inserts it at the specified position into this document, and returns it. + /// + public PdfPage Insert(int index) + { + PdfPage page = new PdfPage(); + Insert(index, page); + return page; + } + + /// + /// Inserts the specified PdfPage at the specified position to this document and maybe returns a new PdfPage object. + /// The value returned is a new object if the inserted page comes from a foreign document. + /// + public PdfPage Insert(int index, PdfPage page) + { + if (page == null) + throw new ArgumentNullException("page"); + + // Is the page already owned by this document? + if (page.Owner == Owner) + { + // Case: Page is first removed and than inserted again, maybe at another position. + int count = Count; + // Check if page is not already part of the document. + for (int idx = 0; idx < count; idx++) + { + if (ReferenceEquals(this[idx], page)) + throw new InvalidOperationException(PSSR.MultiplePageInsert); + } + + // TODO: check this case + // Because the owner of the inserted page is this document we assume that the page was former part of it + // and it is therefore well-defined. + Owner._irefTable.Add(page); + Debug.Assert(page.Owner == Owner); + + // Insert page in array. + PagesArray.Elements.Insert(index, page.Reference); + + // Update page count. + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + + // @PDF/UA: Pages must not be moved. + if (_document._uaManager != null) + _document.Events.OnPageAdded(_document, new PageEventArgs { Page = page, PageIndex = index, EventType = PageEventType.Moved }); + + return page; + } + + // All new page insertions come here. + if (page.Owner == null) + { + // Case: New page was newly created and inserted now. + page.Document = Owner; + + Owner._irefTable.Add(page); + Debug.Assert(page.Owner == Owner); + PagesArray.Elements.Insert(index, page.Reference); + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + + // @PDF/UA: Page was created. + if (_document._uaManager != null) + _document.Events.OnPageAdded(_document, new PageEventArgs { Page = page, PageIndex = index, EventType = PageEventType.Created }); + } + else + { + // Case: Page is from an external document -> import it. + PdfPage importPage = page; + page = ImportExternalPage(importPage); + Owner._irefTable.Add(page); + + // Add page substitute to importedObjectTable. + PdfImportedObjectTable importedObjectTable = Owner.FormTable.GetImportedObjectTable(importPage); + importedObjectTable.Add(importPage.ObjectID, page.Reference); + + PagesArray.Elements.Insert(index, page.Reference); + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + PdfAnnotations.FixImportedAnnotation(page); + + // @PDF/UA: Page was imported. + if (_document._uaManager != null) + _document.Events.OnPageAdded(_document, new PageEventArgs { Page = page, PageIndex = index, EventType = PageEventType.Imported }); + } + if (Owner.Settings.TrimMargins.AreSet) + page.TrimMargins = Owner.Settings.TrimMargins; + + return page; + } + + /// + /// Inserts pages of the specified document into this document. + /// + /// The index in this document where to insert the page . + /// The document to be inserted. + /// The index of the first page to be inserted. + /// The number of pages to be inserted. + public void InsertRange(int index, PdfDocument document, int startIndex, int pageCount) + { + // @PDF/UA + if (document == null) + throw new ArgumentNullException("document"); + + if (index < 0 || index > Count) + throw new ArgumentOutOfRangeException("index", "Argument 'index' out of range."); + + int importDocumentPageCount = document.PageCount; + + if (startIndex < 0 || startIndex + pageCount > importDocumentPageCount) + throw new ArgumentOutOfRangeException("startIndex", "Argument 'startIndex' out of range."); + + if (pageCount > importDocumentPageCount) + throw new ArgumentOutOfRangeException("pageCount", "Argument 'pageCount' out of range."); + + PdfPage[] insertPages = new PdfPage[pageCount]; + PdfPage[] importPages = new PdfPage[pageCount]; + + // 1st create all new pages. + for (int idx = 0, insertIndex = index, importIndex = startIndex; + importIndex < startIndex + pageCount; + idx++, insertIndex++, importIndex++) + { + PdfPage importPage = document.Pages[importIndex]; + PdfPage page = ImportExternalPage(importPage); + insertPages[idx] = page; + importPages[idx] = importPage; + + Owner._irefTable.Add(page); + + // Add page substitute to importedObjectTable. + PdfImportedObjectTable importedObjectTable = Owner.FormTable.GetImportedObjectTable(importPage); + importedObjectTable.Add(importPage.ObjectID, page.Reference); + + PagesArray.Elements.Insert(insertIndex, page.Reference); + + if (Owner.Settings.TrimMargins.AreSet) + page.TrimMargins = Owner.Settings.TrimMargins; + } + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + + // 2nd copy link annotations that are in the range of the imported pages. + for (int idx = 0, importIndex = startIndex; + importIndex < startIndex + pageCount; + idx++, importIndex++) + { + PdfPage importPage = document.Pages[importIndex]; + PdfPage page = insertPages[idx]; + + // Get annotations. + PdfArray annots = importPage.Elements.GetArray(PdfPage.Keys.Annots); + if (annots != null) + { + PdfAnnotations annotations = new PdfAnnotations(Owner); + + // Loop through annotations. + int count = annots.Elements.Count; + for (int idxAnnotation = 0; idxAnnotation < count; idxAnnotation++) + { + PdfDictionary annot = annots.Elements.GetDictionary(idxAnnotation); + if (annot != null) + { + string subtype = annot.Elements.GetString(PdfAnnotation.Keys.Subtype); + if (subtype == "/Link") + { + bool addAnnotation = false; + PdfLinkAnnotation newAnnotation = new PdfLinkAnnotation(Owner); + + PdfName[] importAnnotationKeyNames = annot.Elements.KeyNames; + foreach (PdfName pdfItem in importAnnotationKeyNames) + { + PdfItem impItem; + switch (pdfItem.Value) + { + case "/BS": + newAnnotation.Elements.Add("/BS", new PdfLiteral("<>")); + break; + + case "/F": // /F 4 + impItem = annot.Elements.GetValue("/F"); + Debug.Assert(impItem is PdfInteger); + newAnnotation.Elements.Add("/F", impItem.Clone()); + break; + + case "/Rect": // /Rect [68.6 681.08 145.71 702.53] + impItem = annot.Elements.GetValue("/Rect"); + Debug.Assert(impItem is PdfArray); + newAnnotation.Elements.Add("/Rect", impItem.Clone()); + break; + + case "/StructParent": // /StructParent 3 + impItem = annot.Elements.GetValue("/StructParent"); + Debug.Assert(impItem is PdfInteger); + newAnnotation.Elements.Add("/StructParent", impItem.Clone()); + break; + + case "/Subtype": // Already set. + break; + + case "/Dest": // /Dest [30 0 R /XYZ 68 771 0] + impItem = annot.Elements.GetValue("/Dest"); + impItem = impItem.Clone(); + + // Is value an array with 5 elements where the first one is an iref? + PdfArray destArray = impItem as PdfArray; + if (destArray != null && destArray.Elements.Count == 5) + { + PdfReference iref = destArray.Elements[0] as PdfReference; + if (iref != null) + { + iref = RemapReference(insertPages, importPages, iref); + if (iref != null) + { + destArray.Elements[0] = iref; + newAnnotation.Elements.Add("/Dest", destArray); + addAnnotation = true; + } + } + } + break; + + default: +#if DEBUG_ + Debug-Break.Break(true); +#endif + break; + + } + } + // Add newAnnotations only it points to an imported page. + if (addAnnotation) + annotations.Add(newAnnotation); + } + } + } + + // At least one link annotation found? + if (annotations.Count > 0) + { + //Owner._irefTable.Add(annotations); + page.Elements.Add(PdfPage.Keys.Annots, annotations); + } + } + + } + + // @PDF/UA: Pages were imported. + if (_document._uaManager != null) + _document.Events.OnPageAdded(_document, new PageEventArgs { EventType = PageEventType.Imported }); + } + + /// + /// Inserts all pages of the specified document into this document. + /// + /// The index in this document where to insert the page . + /// The document to be inserted. + public void InsertRange(int index, PdfDocument document) + { + if (document == null) + throw new ArgumentNullException("document"); + + InsertRange(index, document, 0, document.PageCount); + } + + /// + /// Inserts all pages of the specified document into this document. + /// + /// The index in this document where to insert the page . + /// The document to be inserted. + /// The index of the first page to be inserted. + public void InsertRange(int index, PdfDocument document, int startIndex) + { + if (document == null) + throw new ArgumentNullException("document"); + + InsertRange(index, document, startIndex, document.PageCount - startIndex); + } + + /// + /// Removes the specified page from the document. + /// + public void Remove(PdfPage page) + { + PagesArray.Elements.Remove(page.Reference); + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + + // @PDF/UA: Page was removed. + if (_document._uaManager != null) + _document.Events.OnPageRemoved(_document, new PageEventArgs { Page = page, PageIndex = -1, EventType = PageEventType.Removed }); + } + + /// + /// Removes the specified page from the document. + /// + public void RemoveAt(int index) + { + PdfPage page = PagesArray.Elements[index] as PdfPage; + PagesArray.Elements.RemoveAt(index); + Elements.SetInteger(Keys.Count, PagesArray.Elements.Count); + + // @PDF/UA + if (_document._uaManager != null) + _document.Events.OnPageRemoved(_document, new PageEventArgs { Page = page, PageIndex = index }); + } + + /// + /// Moves a page within the page sequence. + /// + /// The page index before this operation. + /// The page index after this operation. + public void MovePage(int oldIndex, int newIndex) + { + // @PDF/UA Not implemented. + if (_document._uaManager != null) + throw new InvalidOperationException("Cannot move a page in a PDF/UA document."); + + if (oldIndex < 0 || oldIndex >= Count) + throw new ArgumentOutOfRangeException("oldIndex"); + if (newIndex < 0 || newIndex >= Count) + throw new ArgumentOutOfRangeException("newIndex"); + if (oldIndex == newIndex) + return; + + //PdfPage page = (PdfPage)pagesArray.Elements[oldIndex]; + PdfReference page = (PdfReference)_pagesArray.Elements[oldIndex]; + _pagesArray.Elements.RemoveAt(oldIndex); + _pagesArray.Elements.Insert(newIndex, page); + } + + /// + /// Imports an external page. The elements of the imported page are cloned and added to this document. + /// Important: In contrast to PdfFormXObject adding an external page always make a deep copy + /// of their transitive closure. Any reuse of already imported objects is not intended because + /// any modification of an imported page must not change another page. + /// + PdfPage ImportExternalPage(PdfPage importPage) + { + if (importPage.Owner._openMode != PdfDocumentOpenMode.Import) + throw new InvalidOperationException("A PDF document must be opened with PdfDocumentOpenMode.Import to import pages from it."); + + PdfPage page = new PdfPage(_document); + + // ReSharper disable AccessToStaticMemberViaDerivedType for a better code readability. + CloneElement(page, importPage, PdfPage.Keys.Resources, false); + CloneElement(page, importPage, PdfPage.Keys.Contents, false); + CloneElement(page, importPage, PdfPage.Keys.MediaBox, true); + CloneElement(page, importPage, PdfPage.Keys.CropBox, true); + CloneElement(page, importPage, PdfPage.Keys.Rotate, true); + CloneElement(page, importPage, PdfPage.Keys.BleedBox, true); + CloneElement(page, importPage, PdfPage.Keys.TrimBox, true); + CloneElement(page, importPage, PdfPage.Keys.ArtBox, true); +#if true + // Do not deep copy annotations. + CloneElement(page, importPage, PdfPage.Keys.Annots, false); +#else + // Deep copy annotations. + CloneElement(page, importPage, PdfPage.Keys.Annots, true); +#endif + // ReSharper restore AccessToStaticMemberViaDerivedType + // TODO more elements? + return page; + } + + /// + /// Helper function for ImportExternalPage. + /// + void CloneElement(PdfPage page, PdfPage importPage, string key, bool deepcopy) + { + Debug.Assert(page != null); + Debug.Assert(page.Owner == _document); + Debug.Assert(importPage.Owner != null); + Debug.Assert(importPage.Owner != _document); + + PdfItem item = importPage.Elements[key]; + if (item != null) + { + PdfImportedObjectTable importedObjectTable = null; + if (!deepcopy) + importedObjectTable = Owner.FormTable.GetImportedObjectTable(importPage); + + // The item can be indirect. If so, replace it by its value. + if (item is PdfReference) + item = ((PdfReference)item).Value; + if (item is PdfObject) + { + PdfObject root = (PdfObject)item; + if (deepcopy) + { + Debug.Assert(root.Owner != null, "See 'else' case for details"); + root = DeepCopyClosure(_document, root); + } + else + { + // The owner can be null if the item is not a reference. + if (root.Owner == null) + root.Document = importPage.Owner; + root = ImportClosure(importedObjectTable, page.Owner, root); + } + + if (root.Reference == null) + page.Elements[key] = root; + else + page.Elements[key] = root.Reference; + } + else + { + // Simple items are just cloned. + page.Elements[key] = item.Clone(); + } + } + } + + static PdfReference RemapReference(PdfPage[] newPages, PdfPage[] impPages, PdfReference iref) + { + // Directs the iref to a one of the imported pages? + for (int idx = 0; idx < newPages.Length; idx++) + { + if (impPages[idx].Reference == iref) + return newPages[idx].Reference; + } + return null; + } + + /// + /// Gets a PdfArray containing all pages of this document. The array must not be modified. + /// + public PdfArray PagesArray + { + get + { + if (_pagesArray == null) + _pagesArray = (PdfArray)Elements.GetValue(Keys.Kids, VCF.Create); + return _pagesArray; + } + } + PdfArray _pagesArray; + + /// + /// Replaces the page tree by a flat array of indirect references to the pages objects. + /// + internal void FlattenPageTree() + { + // Acrobat creates a balanced tree if the number of pages is roughly more than ten. This is + // not difficult but obviously also not necessary. I created a document with 50000 pages with + // PDF4NET and Acrobat opened it in less than 2 seconds. + + //PdfReference xrefRoot = Document.Catalog.Elements[PdfCatalog.Keys.Pages] as PdfReference; + //PdfDictionary[] pages = GetKids(xrefRoot, null); + + // Promote inheritable values down the page tree + PdfPage.InheritedValues values = new PdfPage.InheritedValues(); + PdfPage.InheritValues(this, ref values); + PdfDictionary[] pages = GetKids(Reference, values, null); + + // Replace /Pages in catalog by this object + // xrefRoot.Value = this; + + PdfArray array = new PdfArray(Owner); + foreach (PdfDictionary page in pages) + { + // Fix the parent + page.Elements[PdfPage.Keys.Parent] = Reference; + array.Elements.Add(page.Reference); + } + + Elements.SetName(Keys.Type, "/Pages"); +#if true + // Direct array. + Elements.SetValue(Keys.Kids, array); +#else + // Indirect array. + Document.xrefTable.Add(array); + Elements.SetValue(Keys.Kids, array.XRef); +#endif + Elements.SetInteger(Keys.Count, array.Elements.Count); + } + + /// + /// Recursively converts the page tree into a flat array. + /// + PdfDictionary[] GetKids(PdfReference iref, PdfPage.InheritedValues values, PdfDictionary parent) + { + // TODO: inherit inheritable keys... + PdfDictionary kid = (PdfDictionary)iref.Value; + +#if true + string type = kid.Elements.GetName(Keys.Type); + if (type == "/Page") + { + PdfPage.InheritValues(kid, values); + return new PdfDictionary[] { kid }; + } + + if (string.IsNullOrEmpty(type)) + { + // Type is required. If type is missing, assume it is "/Page" and hope it will work. + // TODO Implement a "Strict" mode in PDFsharp and don't do this in "Strict" mode. + PdfPage.InheritValues(kid, values); + return new PdfDictionary[] { kid }; + } + +#else + if (kid.Elements.GetName(Keys.Type) == "/Page") + { + PdfPage.InheritValues(kid, values); + return new PdfDictionary[] { kid }; + } +#endif + + Debug.Assert(kid.Elements.GetName(Keys.Type) == "/Pages"); + PdfPage.InheritValues(kid, ref values); + List list = new List(); + PdfArray kids = kid.Elements["/Kids"] as PdfArray; + + if (kids == null) + { + PdfReference xref3 = kid.Elements["/Kids"] as PdfReference; + if (xref3 != null) + kids = xref3.Value as PdfArray; + } + + foreach (PdfReference xref2 in kids) + list.AddRange(GetKids(xref2, values, kid)); + int count = list.Count; + Debug.Assert(count == kid.Elements.GetInteger("/Count")); + return list.ToArray(); + } + + /// + /// Prepares the document for saving. + /// + internal override void PrepareForSave() + { + // TODO: Close all open content streams + + // TODO: Create the page tree. + // Arrays have a limit of 8192 entries, but I successfully tested documents + // with 50000 pages and no page tree. + // ==> wait for bug report. + int count = _pagesArray.Elements.Count; + for (int idx = 0; idx < count; idx++) + { + PdfPage page = this[idx]; + page.PrepareForSave(); + } + } + + /// + /// Gets the enumerator. + /// + public new IEnumerator GetEnumerator() + { + return new PdfPagesEnumerator(this); + } + + class PdfPagesEnumerator : IEnumerator + { + internal PdfPagesEnumerator(PdfPages list) + { + _list = list; + _index = -1; + } + + public bool MoveNext() + { + if (_index < _list.Count - 1) + { + _index++; + _currentElement = _list[_index]; + return true; + } + _index = _list.Count; + return false; + } + + public void Reset() + { + _currentElement = null; + _index = -1; + } + + object IEnumerator.Current + { + get { return Current; } + } + + public PdfPage Current + { + get + { + if (_index == -1 || _index >= _list.Count) + throw new InvalidOperationException(PSSR.ListEnumCurrentOutOfRange); + return _currentElement; + } + } + + public void Dispose() + { + // Nothing to do. + } + + PdfPage _currentElement; + int _index; + readonly PdfPages _list; + } + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : PdfPage.InheritablePageKeys + { + /// + /// (Required) The type of PDF object that this dictionary describes; + /// must be Pages for a page tree node. + /// + [KeyInfo(KeyType.Name | KeyType.Required, FixedValue = "Pages")] + public const string Type = "/Type"; + + /// + /// (Required except in root node; must be an indirect reference) + /// The page tree node that is the immediate parent of this one. + /// + [KeyInfo(KeyType.Dictionary | KeyType.Required)] + public const string Parent = "/Parent"; + + /// + /// (Required) An array of indirect references to the immediate children of this node. + /// The children may be page objects or other page tree nodes. + /// + [KeyInfo(KeyType.Array | KeyType.Required)] + public const string Kids = "/Kids"; + + /// + /// (Required) The number of leaf nodes (page objects) that are descendants of this node + /// within the page tree. + /// + [KeyInfo(KeyType.Integer | KeyType.Required)] + public const string Count = "/Count"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfReal.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfReal.cs new file mode 100644 index 00000000..7e9e6457 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfReal.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a direct real value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfReal : PdfNumber + { + /// + /// Initializes a new instance of the class. + /// + public PdfReal() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PdfReal(double value) + { + _value = value; + } + + /// + /// Gets the value as double. + /// + public double Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly double _value; + + /// + /// Returns the real number as string. + /// + public override string ToString() + { + return _value.ToString(Config.SignificantFigures3, CultureInfo.InvariantCulture); + } + + /// + /// Writes the real value with up to three digits. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfRealObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfRealObject.cs new file mode 100644 index 00000000..93b1998c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfRealObject.cs @@ -0,0 +1,95 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect real value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. + /// + public sealed class PdfRealObject : PdfNumberObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfRealObject() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PdfRealObject(double value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + /// The value. + public PdfRealObject(PdfDocument document, double value) + : base(document) + { + _value = value; + } + + /// + /// Gets or sets the value. + /// + public double Value + { + get { return _value; } + set { _value = value; } + } + double _value; + + /// + /// Returns the real as a culture invariant string. + /// + public override string ToString() + { + return _value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Writes the real literal. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(_value); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfRectangle.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfRectangle.cs new file mode 100644 index 00000000..1504969c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfRectangle.cs @@ -0,0 +1,499 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows.Media; +#endif +using PdfSharp.Drawing; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a PDF rectangle value, that is internally an array with 4 real values. + /// + [DebuggerDisplay("{DebuggerDisplay}")] + public sealed class PdfRectangle : PdfItem + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + + /// + /// Initializes a new instance of the PdfRectangle class. + /// + public PdfRectangle() + { } + + /// + /// Initializes a new instance of the PdfRectangle class with two points specifying + /// two diagonally opposite corners. Notice that in contrast to GDI+ convention the + /// 3rd and the 4th parameter specify a point and not a width. This is so much confusing + /// that this function is for internal use only. + /// + internal PdfRectangle(double x1, double y1, double x2, double y2) + { + _x1 = x1; + _y1 = y1; + _x2 = x2; + _y2 = y2; + } + +#if GDI + /// + /// Initializes a new instance of the PdfRectangle class with two points specifying + /// two diagonally opposite corners. + /// + public PdfRectangle(PointF pt1, PointF pt2) + { + _x1 = pt1.X; + _y1 = pt1.Y; + _x2 = pt2.X; + _y2 = pt2.Y; + } +#endif + + /// + /// Initializes a new instance of the PdfRectangle class with two points specifying + /// two diagonally opposite corners. + /// + public PdfRectangle(XPoint pt1, XPoint pt2) + { + _x1 = pt1.X; + _y1 = pt1.Y; + _x2 = pt2.X; + _y2 = pt2.Y; + } + +#if GDI + /// + /// Initializes a new instance of the PdfRectangle class with the specified location and size. + /// + public PdfRectangle(PointF pt, SizeF size) + { + _x1 = pt.X; + _y1 = pt.Y; + _x2 = pt.X + size.Width; + _y2 = pt.Y + size.Height; + } +#endif + + /// + /// Initializes a new instance of the PdfRectangle class with the specified location and size. + /// + public PdfRectangle(XPoint pt, XSize size) + { + _x1 = pt.X; + _y1 = pt.Y; + _x2 = pt.X + size.Width; + _y2 = pt.Y + size.Height; + } + + /// + /// Initializes a new instance of the PdfRectangle class with the specified XRect. + /// + public PdfRectangle(XRect rect) + { + _x1 = rect.X; + _y1 = rect.Y; + _x2 = rect.X + rect.Width; + _y2 = rect.Y + rect.Height; + } + + /// + /// Initializes a new instance of the PdfRectangle class with the specified PdfArray. + /// + internal PdfRectangle(PdfItem item) + { + if (item == null || item is PdfNull) + return; + + if (item is PdfReference) + item = ((PdfReference)item).Value; + + PdfArray array = item as PdfArray; + if (array == null) + throw new InvalidOperationException(PSSR.UnexpectedTokenInPdfFile); + + _x1 = array.Elements.GetReal(0); + _y1 = array.Elements.GetReal(1); + _x2 = array.Elements.GetReal(2); + _y2 = array.Elements.GetReal(3); + } + + /// + /// Clones this instance. + /// + public new PdfRectangle Clone() + { + return (PdfRectangle)Copy(); + } + + /// + /// Implements cloning this instance. + /// + protected override object Copy() + { + PdfRectangle rect = (PdfRectangle)base.Copy(); + return rect; + } + + /// + /// Tests whether all coordinate are zero. + /// + public bool IsEmpty + { + // ReSharper disable CompareOfFloatsByEqualityOperator + get { return _x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0; } + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Tests whether the specified object is a PdfRectangle and has equal coordinates. + /// + public override bool Equals(object obj) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + PdfRectangle rectangle = obj as PdfRectangle; + if (rectangle != null) + { + PdfRectangle rect = rectangle; + return rect._x1 == _x1 && rect._y1 == _y1 && rect._x2 == _x2 && rect._y2 == _y2; + } + return false; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Serves as a hash function for a particular type. + /// + public override int GetHashCode() + { + // This code is from System.Drawing... + return (int)(((((uint)_x1) ^ ((((uint)_y1) << 13) | + (((uint)_y1) >> 0x13))) ^ ((((uint)_x2) << 0x1a) | + (((uint)_x2) >> 6))) ^ ((((uint)_y2) << 7) | + (((uint)_y2) >> 0x19))); + } + + /// + /// Tests whether two structures have equal coordinates. + /// + public static bool operator ==(PdfRectangle left, PdfRectangle right) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + // use: if (Object.ReferenceEquals(left, null)) + if ((object)left != null) + { + if ((object)right != null) + return left._x1 == right._x1 && left._y1 == right._y1 && left._x2 == right._x2 && left._y2 == right._y2; + return false; + } + return (object)right == null; + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + /// Tests whether two structures differ in one or more coordinates. + /// + public static bool operator !=(PdfRectangle left, PdfRectangle right) + { + return !(left == right); + } + + /// + /// Gets or sets the x-coordinate of the first corner of this PdfRectangle. + /// + public double X1 + { + get { return _x1; } + } + readonly double _x1; + + /// + /// Gets or sets the y-coordinate of the first corner of this PdfRectangle. + /// + public double Y1 + { + get { return _y1; } + } + readonly double _y1; + + /// + /// Gets or sets the x-coordinate of the second corner of this PdfRectangle. + /// + public double X2 + { + get { return _x2; } + } + readonly double _x2; + + /// + /// Gets or sets the y-coordinate of the second corner of this PdfRectangle. + /// + public double Y2 + { + get { return _y2; } + } + readonly double _y2; + + /// + /// Gets X2 - X1. + /// + public double Width + { + get { return _x2 - _x1; } + } + + /// + /// Gets Y2 - Y1. + /// + public double Height + { + get { return _y2 - _y1; } + } + + /// + /// Gets or sets the coordinates of the first point of this PdfRectangle. + /// + public XPoint Location + { + get { return new XPoint(_x1, _y1); } + } + + /// + /// Gets or sets the size of this PdfRectangle. + /// + public XSize Size + { + get { return new XSize(_x2 - _x1, _y2 - _y1); } + } + +#if GDI + /// + /// Determines if the specified point is contained within this PdfRectangle. + /// + public bool Contains(PointF pt) + { + return Contains(pt.X, pt.Y); + } +#endif + + /// + /// Determines if the specified point is contained within this PdfRectangle. + /// + public bool Contains(XPoint pt) + { + return Contains(pt.X, pt.Y); + } + + /// + /// Determines if the specified point is contained within this PdfRectangle. + /// + public bool Contains(double x, double y) + { + // Treat rectangle inclusive/inclusive. + return _x1 <= x && x <= _x2 && _y1 <= y && y <= _y2; + } + +#if GDI + /// + /// Determines if the rectangular region represented by rect is entirely contained within this PdfRectangle. + /// + public bool Contains(RectangleF rect) + { + return _x1 <= rect.X && (rect.X + rect.Width) <= _x2 && + _y1 <= rect.Y && (rect.Y + rect.Height) <= _y2; + } +#endif + + /// + /// Determines if the rectangular region represented by rect is entirely contained within this PdfRectangle. + /// + public bool Contains(XRect rect) + { + return _x1 <= rect.X && (rect.X + rect.Width) <= _x2 && + _y1 <= rect.Y && (rect.Y + rect.Height) <= _y2; + } + + /// + /// Determines if the rectangular region represented by rect is entirely contained within this PdfRectangle. + /// + public bool Contains(PdfRectangle rect) + { + return _x1 <= rect._x1 && rect._x2 <= _x2 && + _y1 <= rect._y1 && rect._y2 <= _y2; + } + + /// + /// Returns the rectangle as an XRect object. + /// + public XRect ToXRect() + { + return new XRect(_x1, _y1, Width, Height); + } + + /// + /// Returns the rectangle as a string in the form [x1 y1 x2 y2]. + /// + public override string ToString() + { + const string format = Config.SignificantFigures3; + return PdfEncoders.Format("[{0:" + format + "} {1:" + format + "} {2:" + format + "} {3:" + format + "}]", _x1, _y1, _x2, _y2); + } + + /// + /// Writes the rectangle. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + + /// + /// Gets the DebuggerDisplayAttribute text. + /// + // ReSharper disable UnusedMember.Local + string DebuggerDisplay + // ReSharper restore UnusedMember.Local + { + get + { + const string format = Config.SignificantFigures10; + return String.Format(CultureInfo.InvariantCulture, + "X1={0:" + format + "}, Y1={1:" + format + "}, X2={2:" + format + "}, Y2={3:" + format + "}", _x1, _y1, _x2, _y2); + } + } + +#if false // This object is considered as immutable. + // /// + // /// Adjusts the location of this PdfRectangle by the specified amount. + // /// + // public void Offset(PointF pos) + // { + // Offset(pos.X, pos.Y); + // } + // + // /// + // /// Adjusts the location of this PdfRectangle by the specified amount. + // /// + // public void Offset(double x, double y) + // { + // _x1 += x; + // _y1 += y; + // _x2 += x; + // _y2 += y; + // } + // + // /// + // /// Inflates this PdfRectangle by the specified amount. + // /// + // public void Inflate(double x, double y) + // { + // _x1 -= x; + // _y1 -= y; + // _x2 += x; + // _y2 += y; + // } + // + // /// + // /// Inflates this PdfRectangle by the specified amount. + // /// + // public void Inflate(SizeF size) + // { + // Inflate(size.Width, size.Height); + // } + // + // /// + // /// Creates and returns an inflated copy of the specified PdfRectangle. + // /// + // public static PdfRectangle Inflate(PdfRectangle rect, double x, double y) + // { + // rect.Inflate(x, y); + // return rect; + // } + // + // /// + // /// Replaces this PdfRectangle with the intersection of itself and the specified PdfRectangle. + // /// + // public void Intersect(PdfRectangle rect) + // { + // PdfRectangle rect2 = PdfRectangle.Intersect(rect, this); + // _x1 = rect2.x1; + // _y1 = rect2.y1; + // _x2 = rect2.x2; + // _y2 = rect2.y2; + // } + // + // /// + // /// Returns a PdfRectangle that represents the intersection of two rectangles. If there is no intersection, + // /// an empty PdfRectangle is returned. + // /// + // public static PdfRectangle Intersect(PdfRectangle rect1, PdfRectangle rect2) + // { + // double xx1 = Math.Max(rect1.x1, rect2.x1); + // double xx2 = Math.Min(rect1.x2, rect2.x2); + // double yy1 = Math.Max(rect1.y1, rect2.y1); + // double yy2 = Math.Min(rect1.y2, rect2.y2); + // if (xx2 >= xx1 && yy2 >= yy1) + // return new PdfRectangle(xx1, yy1, xx2, yy2); + // return PdfRectangle.Empty; + // } + // + // /// + // /// Determines if this rectangle intersects with the specified PdfRectangle. + // /// + // public bool IntersectsWith(PdfRectangle rect) + // { + // return rect.x1 < _x2 && _x1 < rect.x2 && rect.y1 < _y2 && _y1 < rect.y2; + // } + // + // /// + // /// Creates the smallest rectangle that can contain both of two specified rectangles. + // /// + // public static PdfRectangle Union(PdfRectangle rect1, PdfRectangle rect2) + // { + // return new PdfRectangle( + // Math.Min(rect1.x1, rect2.x1), Math.Max(rect1.x2, rect2.x2), + // Math.Min(rect1.y1, rect2.y1), Math.Max(rect1.y2, rect2.y2)); + // } +#endif + + /// + /// Represents an empty PdfRectangle. + /// + public static readonly PdfRectangle Empty = new PdfRectangle(); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfReferenceTable.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfReferenceTable.cs new file mode 100644 index 00000000..a1a6924f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfReferenceTable.cs @@ -0,0 +1,560 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using PdfSharp.Pdf.Advanced; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + // NOT YET IN USE ANYMORE. REPLACEED PdfSharp.Pdf.Advanced.PdfCrossReferenceTable. + + /// + /// Represents the cross-reference table of a PDF document. + /// It contains all indirect objects of a document. + /// + internal sealed class PdfReferenceTable_old // Must not be derive from PdfObject. + { + public PdfReferenceTable_old(PdfDocument document) + { + _document = document; + } + readonly PdfDocument _document; + + /// + /// Represents the relation between PdfObjectID and PdfReference for a PdfDocument. + /// + public Dictionary ObjectTable = new Dictionary(); + + internal bool IsUnderConstruction + { + get { return _isUnderConstruction; } + set { _isUnderConstruction = value; } + } + bool _isUnderConstruction; + + /// + /// Adds a cross reference entry to the table. Used when parsing the trailer. + /// + public void Add(PdfReference iref) + { + if (iref.ObjectID.IsEmpty) + iref.ObjectID = new PdfObjectID(GetNewObjectNumber()); + + if (ObjectTable.ContainsKey(iref.ObjectID)) + throw new InvalidOperationException("Object already in table."); + + ObjectTable.Add(iref.ObjectID, iref); + } + + /// + /// Adds a PdfObject to the table. + /// + public void Add(PdfObject value) + { + if (value.Owner == null) + value.Document = _document; + else + Debug.Assert(value.Owner == _document); + + if (value.ObjectID.IsEmpty) + value.SetObjectID(GetNewObjectNumber(), 0); + + if (ObjectTable.ContainsKey(value.ObjectID)) + throw new InvalidOperationException("Object already in table."); + + ObjectTable.Add(value.ObjectID, value.Reference); + } + + public void Remove(PdfReference iref) + { + ObjectTable.Remove(iref.ObjectID); + } + + /// + /// Gets a cross reference entry from an object identifier. + /// Returns null if no object with the specified ID exists in the object table. + /// + public PdfReference this[PdfObjectID objectID] + { + get + { + PdfReference iref; + ObjectTable.TryGetValue(objectID, out iref); + return iref; + } + } + + /// + /// Indicates whether the specified object identifier is in the table. + /// + public bool Contains(PdfObjectID objectID) + { + return ObjectTable.ContainsKey(objectID); + } + + //public PdfObject GetObject(PdfObjectID objectID) + //{ + // return this[objectID].Value; + //} + + // /// + // /// Gets the entry for the specified object, or null, if the object is not in + // /// this XRef table. + // /// + // internal PdfReference GetEntry(PdfObjectID objectID) + // { + // return this[objectID]; + // } + + /// + /// Returns the next free object number. + /// + public int GetNewObjectNumber() + { + // New objects are numbered consecutively. If a document is imported, maxObjectNumber is + // set to the highest object number used in the document. + return ++_maxObjectNumber; + } + internal int _maxObjectNumber; + + /// + /// Writes the xref section in pdf stream. + /// + internal void WriteObject(PdfWriter writer) + { + writer.WriteRaw("xref\n"); + + PdfReference[] irefs = AllReferences; + + int count = irefs.Length; + writer.WriteRaw(String.Format("0 {0}\n", count + 1)); + writer.WriteRaw(String.Format("{0:0000000000} {1:00000} {2} \n", 0, 65535, "f")); + //PdfEncoders.WriteAnsi(stream, text); + + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; + + // Acrobat is very pedantic; it must be exactly 20 bytes per line. + writer.WriteRaw(String.Format("{0:0000000000} {1:00000} {2} \n", iref.Position, iref.GenerationNumber, "n")); + } + } + + /// + /// Gets an array of all object identifier. For debugging purposes only. + /// + internal PdfObjectID[] AllObjectIDs + { + get + { + ICollection collection = ObjectTable.Keys; + PdfObjectID[] objectIDs = new PdfObjectID[collection.Count]; + collection.CopyTo(objectIDs, 0); + return objectIDs; + } + } + + /// + /// Gets an array of all cross references in ascending order by their object identifier. + /// + internal PdfReference[] AllReferences + { + get + { + Dictionary.ValueCollection collection = ObjectTable.Values; + List list = new List(collection); + list.Sort(PdfReference.Comparer); + PdfReference[] irefs = new PdfReference[collection.Count]; + list.CopyTo(irefs, 0); + return irefs; + } + } + + internal void HandleOrphanedReferences() + { } + + /// + /// Removes all objects that cannot be reached from the trailer. + /// Returns the number of removed objects. + /// + internal int Compact() + { + // TODO: remove PdfBooleanObject, PdfIntegerObject etc. + int removed = ObjectTable.Count; + //CheckConsistence(); + // TODO: Is this really so easy? + PdfReference[] irefs = TransitiveClosure(_document._trailer); + +#if DEBUG + // Have any two objects the same ID? + Dictionary ids = new Dictionary(); + foreach (PdfObjectID objID in ObjectTable.Keys) + { + ids.Add(objID.ObjectNumber, 0); + } + + // Have any two irefs the same value? + //Dictionary ids = new Dictionary(); + ids.Clear(); + foreach (PdfReference iref in ObjectTable.Values) + { + ids.Add(iref.ObjectNumber, 0); + } + + // + Dictionary refs = new Dictionary(); + foreach (PdfReference iref in irefs) + { + refs.Add(iref, 0); + } + foreach (PdfReference value in ObjectTable.Values) + { + if (!refs.ContainsKey(value)) + value.GetType(); + } + + foreach (PdfReference iref in ObjectTable.Values) + { + if (iref.Value == null) + GetType(); + Debug.Assert(iref.Value != null); + } + + foreach (PdfReference iref in irefs) + { + if (!ObjectTable.ContainsKey(iref.ObjectID)) + GetType(); + Debug.Assert(ObjectTable.ContainsKey(iref.ObjectID)); + + if (iref.Value == null) + GetType(); + Debug.Assert(iref.Value != null); + } +#endif + + _maxObjectNumber = 0; + ObjectTable.Clear(); + foreach (PdfReference iref in irefs) + { + // This if is needed for corrupt PDF files from the wild. + // Without the if, an exception will be thrown if the file contains duplicate IDs ("An item with the same key has already been added to the dictionary."). + // With the if, the first object with the ID will be used and later objects with the same ID will be ignored. + if (!ObjectTable.ContainsKey(iref.ObjectID)) + { + ObjectTable.Add(iref.ObjectID, iref); + _maxObjectNumber = Math.Max(_maxObjectNumber, iref.ObjectNumber); + } + } + //CheckConsistence(); + removed -= ObjectTable.Count; + return removed; + } + + /// + /// Renumbers the objects starting at 1. + /// + internal void Renumber() + { + //CheckConsistence(); + PdfReference[] irefs = AllReferences; + ObjectTable.Clear(); + // Give all objects a new number. + int count = irefs.Length; + for (int idx = 0; idx < count; idx++) + { + PdfReference iref = irefs[idx]; +#if DEBUG_ + if (iref.ObjectNumber == 1108) + GetType(); +#endif + iref.ObjectID = new PdfObjectID(idx + 1); + // Rehash with new number. + ObjectTable.Add(iref.ObjectID, iref); + } + _maxObjectNumber = count; + //CheckConsistence(); + } + + /// + /// Checks the logical consistence for debugging purposes (useful after reconstruction work). + /// + [Conditional("DEBUG_")] + public void CheckConsistence() + { + Dictionary ht1 = new Dictionary(); + foreach (PdfReference iref in ObjectTable.Values) + { + Debug.Assert(!ht1.ContainsKey(iref), "Duplicate iref."); + Debug.Assert(iref.Value != null); + ht1.Add(iref, null); + } + + Dictionary ht2 = new Dictionary(); + foreach (PdfReference iref in ObjectTable.Values) + { + Debug.Assert(!ht2.ContainsKey(iref.ObjectID), "Duplicate iref."); + ht2.Add(iref.ObjectID, null); + } + + ICollection collection = ObjectTable.Values; + int count = collection.Count; + PdfReference[] irefs = new PdfReference[count]; + collection.CopyTo(irefs, 0); +#if true + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) + if (i != j) + { + Debug.Assert(ReferenceEquals(irefs[i].Document, _document)); + Debug.Assert(irefs[i] != irefs[j]); + Debug.Assert(!ReferenceEquals(irefs[i], irefs[j])); + Debug.Assert(!ReferenceEquals(irefs[i].Value, irefs[j].Value)); + Debug.Assert(!Equals(irefs[i].ObjectID, irefs[j].Value.ObjectID)); + Debug.Assert(irefs[i].ObjectNumber != irefs[j].Value.ObjectNumber); + Debug.Assert(ReferenceEquals(irefs[i].Document, irefs[j].Document)); + GetType(); + } +#endif + } + + ///// + ///// The garbage collector for PDF objects. + ///// + //public sealed class GC + //{ + // PdfXRefTable xrefTable; + // + // internal GC(PdfXRefTable xrefTable) + // { + // _xrefTable = xrefTable; + // } + // + // public void Collect() + // { } + // + // public PdfReference[] ReachableObjects() + // { + // Hash_table objects = new Hash_table(); + // TransitiveClosure(objects, _xrefTable.document.trailer); + // } + + /// + /// Calculates the transitive closure of the specified PdfObject, i.e. all indirect objects + /// recursively reachable from the specified object. + /// + public PdfReference[] TransitiveClosure(PdfObject pdfObject) + { + return TransitiveClosure(pdfObject, Int16.MaxValue); + } + + /// + /// Calculates the transitive closure of the specified PdfObject with the specified depth, i.e. all indirect objects + /// recursively reachable from the specified object in up to maximally depth steps. + /// + public PdfReference[] TransitiveClosure(PdfObject pdfObject, int depth) + { + CheckConsistence(); + Dictionary objects = new Dictionary(); + _overflow = new Dictionary(); + TransitiveClosureImplementation(objects, pdfObject /*, ref depth*/); + TryAgain: + if (_overflow.Count > 0) + { + PdfObject[] array = new PdfObject[_overflow.Count]; + _overflow.Keys.CopyTo(array, 0); + _overflow = new Dictionary(); + for (int idx = 0; idx < array.Length; idx++) + { + PdfObject obj = array[idx]; + TransitiveClosureImplementation(objects, obj /*, ref depth*/); + } + goto TryAgain; + } + + CheckConsistence(); + + ICollection collection = objects.Keys; + int count = collection.Count; + PdfReference[] irefs = new PdfReference[count]; + collection.CopyTo(irefs, 0); + +#if true_ + for (int i = 0; i < count; i++) + for (int j = 0; j < count; j++) + if (i != j) + { + Debug.Assert(ReferenceEquals(irefs[i].Document, _document)); + Debug.Assert(irefs[i] != irefs[j]); + Debug.Assert(!ReferenceEquals(irefs[i], irefs[j])); + Debug.Assert(!ReferenceEquals(irefs[i].Value, irefs[j].Value)); + Debug.Assert(!Equals(irefs[i].ObjectID, irefs[j].Value.ObjectID)); + Debug.Assert(irefs[i].ObjectNumber != irefs[j].Value.ObjectNumber); + Debug.Assert(ReferenceEquals(irefs[i].Document, irefs[j].Document)); + GetType(); + } +#endif + return irefs; + } + + static int _nestingLevel; + Dictionary _overflow = new Dictionary(); + + void TransitiveClosureImplementation(Dictionary objects, PdfObject pdfObject/*, ref int depth*/) + { + try + { + _nestingLevel++; + if (_nestingLevel >= 1000) + { + if (!_overflow.ContainsKey(pdfObject)) + _overflow.Add(pdfObject, null); + return; + } +#if DEBUG_ + //enterCount++; + if (enterCount == 5400) + GetType(); + //if (!Object.ReferenceEquals(pdfObject.Owner, _document)) + // GetType(); + //////Debug.Assert(Object.ReferenceEquals(pdfObject27.Document, _document)); + // if (item is PdfObject && ((PdfObject)item).ObjectID.ObjectNumber == 5) + // Debug.WriteLine("items: " + ((PdfObject)item).ObjectID.ToString()); + //if (pdfObject.ObjectNumber == 5) + // GetType(); +#endif + + IEnumerable enumerable = null; //(IEnumerator)pdfObject; + PdfDictionary dict; + PdfArray array; + if ((dict = pdfObject as PdfDictionary) != null) + enumerable = dict.Elements.Values; + else if ((array = pdfObject as PdfArray) != null) + enumerable = array.Elements; + else + Debug.Assert(false, "Should not come here."); + + if (enumerable != null) + { + foreach (PdfItem item in enumerable) + { + PdfReference iref = item as PdfReference; + if (iref != null) + { + // Is this an indirect reference to an object that does not exist? + //if (iref.Document == null) + //{ + // Debug.WriteLine("Dead object detected: " + iref.ObjectID.ToString()); + // PdfReference dead = DeadObject; + // iref.ObjectID = dead.ObjectID; + // iref.Document = _document; + // iref.SetObject(dead.Value); + // PdfDictionary dict = (PdfDictionary)dead.Value; + + // dict.Elements["/DeadObjectCount"] = + // new PdfInteger(dict.Elements.GetInteger("/DeadObjectCount") + 1); + + // iref = dead; + //} + + if (!ReferenceEquals(iref.Document, _document)) + { + GetType(); + Debug.WriteLine(String.Format("Bad iref: {0}", iref.ObjectID.ToString())); + } + Debug.Assert(ReferenceEquals(iref.Document, _document) || iref.Document == null, "External object detected!"); +#if DEBUG_ + if (iref.ObjectID.ObjectNumber == 23) + GetType(); +#endif + if (!objects.ContainsKey(iref)) + { + PdfObject value = iref.Value; + + // Ignore unreachable objects. + if (iref.Document != null) + { + // ... from trailer hack + if (value == null) + { + iref = ObjectTable[iref.ObjectID]; + Debug.Assert(iref.Value != null); + value = iref.Value; + } + Debug.Assert(ReferenceEquals(iref.Document, _document)); + objects.Add(iref, null); + //Debug.WriteLine(String.Format("objects.Add('{0}', null);", iref.ObjectID.ToString())); + if (value is PdfArray || value is PdfDictionary) + TransitiveClosureImplementation(objects, value /*, ref depth*/); + } + //else + //{ + // objects2.Add(this[iref.ObjectID], null); + //} + } + } + else + { + PdfObject pdfObject28 = item as PdfObject; + //if (pdfObject28 != null) + // Debug.Assert(Object.ReferenceEquals(pdfObject28.Document, _document)); + if (pdfObject28 != null && (pdfObject28 is PdfDictionary || pdfObject28 is PdfArray)) + TransitiveClosureImplementation(objects, pdfObject28 /*, ref depth*/); + } + } + } + } + finally + { + _nestingLevel--; + } + } + + /// + /// Gets the cross reference to an objects used for undefined indirect references. + /// + public PdfReference DeadObject + { + get + { + if (_deadObject == null) + { + _deadObject = new PdfDictionary(_document); + Add(_deadObject); + _deadObject.Elements.Add("/DeadObjectCount", new PdfInteger()); + } + return _deadObject.Reference; + } + } + PdfDictionary _deadObject; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfString.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfString.cs new file mode 100644 index 00000000..e97d1913 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfString.cs @@ -0,0 +1,329 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Text; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + // TODO: Make code more readeable with PDF 1.7 strings: text string, ASCII string, byte string etc. + + /// + /// Determines the encoding of a PdfString or PdfStringObject. + /// + [Flags] + public enum PdfStringEncoding + { + /// + /// The characters of the string are actually bytes with an unknown or context specific meaning or encoding. + /// With this encoding the 8 high bits of each character is zero. + /// + RawEncoding = PdfStringFlags.RawEncoding, + + /// + /// Not yet used by PDFsharp. + /// + StandardEncoding = PdfStringFlags.StandardEncoding, + + /// + /// The characters of the string are actually bytes with PDF document encoding. + /// With this encoding the 8 high bits of each character is zero. + /// + // ReSharper disable InconsistentNaming because the name is spelled as in the Adobe reference. + PDFDocEncoding = PdfStringFlags.PDFDocEncoding, + // ReSharper restore InconsistentNaming + + /// + /// The characters of the string are actually bytes with Windows ANSI encoding. + /// With this encoding the 8 high bits of each character is zero. + /// + WinAnsiEncoding = PdfStringFlags.WinAnsiEncoding, + + /// + /// Not yet used by PDFsharp. + /// + MacRomanEncoding = PdfStringFlags.MacExpertEncoding, + + /// + /// Not yet used by PDFsharp. + /// + MacExpertEncoding = PdfStringFlags.MacExpertEncoding, + + /// + /// The characters of the string are Unicode characters. + /// + Unicode = PdfStringFlags.Unicode, + } + + /// + /// Internal wrapper for PdfStringEncoding. + /// + [Flags] + enum PdfStringFlags + { + // ReSharper disable InconsistentNaming + RawEncoding = 0x00, + StandardEncoding = 0x01, // not used by PDFsharp + PDFDocEncoding = 0x02, + WinAnsiEncoding = 0x03, + MacRomanEncoding = 0x04, // not used by PDFsharp + MacExpertEncoding = 0x05, // not used by PDFsharp + Unicode = 0x06, + EncodingMask = 0x0F, + + HexLiteral = 0x80, + // ReSharper restore InconsistentNaming + } + + /// + /// Represents a direct text string value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfString : PdfItem + { + /// + /// Initializes a new instance of the class. + /// + public PdfString() + { + // Redundant assignment. + //_flags = PdfStringFlags.RawEncoding; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PdfString(string value) + { +#if true + if (!IsRawEncoding(value)) + _flags = PdfStringFlags.Unicode; + _value = value; +#else + CheckRawEncoding(value); + _value = value; + //_flags = PdfStringFlags.RawEncoding; +#endif + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The encoding. + public PdfString(string value, PdfStringEncoding encoding) + { + switch (encoding) + { + case PdfStringEncoding.RawEncoding: + CheckRawEncoding(value); + break; + + case PdfStringEncoding.StandardEncoding: + break; + + case PdfStringEncoding.PDFDocEncoding: + break; + + case PdfStringEncoding.WinAnsiEncoding: + CheckRawEncoding(value); + break; + + case PdfStringEncoding.MacRomanEncoding: + break; + + case PdfStringEncoding.Unicode: + break; + + default: + throw new ArgumentOutOfRangeException("encoding"); + } + _value = value; + //if ((flags & PdfStringFlags.EncodingMask) == 0) + // flags |= PdfStringFlags.PDFDocEncoding; + _flags = (PdfStringFlags)encoding; + } + + internal PdfString(string value, PdfStringFlags flags) + { + _value = value; + _flags = flags; + } + + /// + /// Gets the number of characters in this string. + /// + public int Length + { + get { return _value == null ? 0 : _value.Length; } + } + + /// + /// Gets the encoding. + /// + public PdfStringEncoding Encoding + { + get { return (PdfStringEncoding)(_flags & PdfStringFlags.EncodingMask); } + } + + /// + /// Gets a value indicating whether the string is a hexadecimal literal. + /// + public bool HexLiteral + { + get { return (_flags & PdfStringFlags.HexLiteral) != 0; } + } + + internal PdfStringFlags Flags + { + get { return _flags; } + } + readonly PdfStringFlags _flags; + + /// + /// Gets the string value. + /// + public string Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value ?? ""; } + } + string _value; + + /// + /// Gets or sets the string value for encryption purposes. + /// + internal byte[] EncryptionValue + { + // TODO: Unicode case is not handled! + get { return _value == null ? new byte[0] : PdfEncoders.RawEncoding.GetBytes(_value); } + // BUG: May lead to trouble with the value semantics of PdfString + set { _value = PdfEncoders.RawEncoding.GetString(value, 0, value.Length); } + } + + /// + /// Returns the string. + /// + public override string ToString() + { +#if true + PdfStringEncoding encoding = (PdfStringEncoding)(_flags & PdfStringFlags.EncodingMask); + string pdf = (_flags & PdfStringFlags.HexLiteral) == 0 ? + PdfEncoders.ToStringLiteral(_value, encoding, null) : + PdfEncoders.ToHexStringLiteral(_value, encoding, null); + return pdf; +#else + return _value; +#endif + } + + /// + /// Hack for document encoded bookmarks. + /// + public string ToStringFromPdfDocEncoded() + { + int length = _value.Length; + char[] bytes = new char[length]; + for (int idx = 0; idx < length; idx++) + { + char ch = _value[idx]; + if (ch <= 255) + { + bytes[idx] = Encode[ch]; + } + else + { + //Debug-Break.Break(); + throw new InvalidOperationException("DocEncoded string contains char greater 255."); + } + } + StringBuilder sb = new StringBuilder(length); + for (int idx = 0; idx < length; idx++) + sb.Append((char)bytes[idx]); + return sb.ToString(); + } + static readonly char[] Encode = + { + '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', '\x09', '\x0A', '\x0B', '\x0C', '\x0D', '\x0E', '\x0F', + '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1A', '\x1B', '\x1C', '\x1D', '\x1E', '\x1F', + '\x20', '\x21', '\x22', '\x23', '\x24', '\x25', '\x26', '\x27', '\x28', '\x29', '\x2A', '\x2B', '\x2C', '\x2D', '\x2E', '\x2F', + '\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39', '\x3A', '\x3B', '\x3C', '\x3D', '\x3E', '\x3F', + '\x40', '\x41', '\x42', '\x43', '\x44', '\x45', '\x46', '\x47', '\x48', '\x49', '\x4A', '\x4B', '\x4C', '\x4D', '\x4E', '\x4F', + '\x50', '\x51', '\x52', '\x53', '\x54', '\x55', '\x56', '\x57', '\x58', '\x59', '\x5A', '\x5B', '\x5C', '\x5D', '\x5E', '\x5F', + '\x60', '\x61', '\x62', '\x63', '\x64', '\x65', '\x66', '\x67', '\x68', '\x69', '\x6A', '\x6B', '\x6C', '\x6D', '\x6E', '\x6F', + '\x70', '\x71', '\x72', '\x73', '\x74', '\x75', '\x76', '\x77', '\x78', '\x79', '\x7A', '\x7B', '\x7C', '\x7D', '\x7E', '\x7F', + '\x2022', '\x2020', '\x2021', '\x2026', '\x2014', '\x2013', '\x0192', '\x2044', '\x2039', '\x203A', '\x2212', '\x2030', '\x201E', '\x201C', '\x201D', '\x2018', + '\x2019', '\x201A', '\x2122', '\xFB01', '\xFB02', '\x0141', '\x0152', '\x0160', '\x0178', '\x017D', '\x0131', '\x0142', '\x0153', '\x0161', '\x017E', '\xFFFD', + '\x20AC', '\xA1', '\xA2', '\xA3', '\xA4', '\xA5', '\xA6', '\xA7', '\xA8', '\xA9', '\xAA', '\xAB', '\xAC', '\xAD', '\xAE', '\xAF', + '\xB0', '\xB1', '\xB2', '\xB3', '\xB4', '\xB5', '\xB6', '\xB7', '\xB8', '\xB9', '\xBA', '\xBB', '\xBC', '\xBD', '\xBE', '\xBF', + '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF', + '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF', + '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF', + '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', '\xFF', + }; + + static void CheckRawEncoding(string s) + { + if (String.IsNullOrEmpty(s)) + return; + + int length = s.Length; + for (int idx = 0; idx < length; idx++) + { + Debug.Assert(s[idx] < 256, "RawString contains invalid character."); + } + } + + static bool IsRawEncoding(string s) + { + if (String.IsNullOrEmpty(s)) + return true; + + int length = s.Length; + for (int idx = 0; idx < length; idx++) + { + if (!(s[idx] < 256)) + return false; + } + return true; + } + + /// + /// Writes the string DocEncoded. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfStringObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfStringObject.cs new file mode 100644 index 00000000..d287ce31 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfStringObject.cs @@ -0,0 +1,149 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using PdfSharp.Pdf.IO; +using PdfSharp.Pdf.Internal; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect text string value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfStringObject : PdfObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfStringObject() + { + _flags = PdfStringFlags.RawEncoding; + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + /// The value. + public PdfStringObject(PdfDocument document, string value) + : base(document) + { + _value = value; + _flags = PdfStringFlags.RawEncoding; + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The encoding. + public PdfStringObject(string value, PdfStringEncoding encoding) + { + _value = value; + //if ((flags & PdfStringFlags.EncodingMask) == 0) + // flags |= PdfStringFlags.PDFDocEncoding; + _flags = (PdfStringFlags)encoding; + } + + internal PdfStringObject(string value, PdfStringFlags flags) + { + _value = value; + //if ((flags & PdfStringFlags.EncodingMask) == 0) + // flags |= PdfStringFlags.PDFDocEncoding; + _flags = flags; + } + + /// + /// Gets the number of characters in this string. + /// + public int Length + { + get { return _value == null ? 0 : _value.Length; } + } + + /// + /// Gets or sets the encoding. + /// + public PdfStringEncoding Encoding + { + get { return (PdfStringEncoding)(_flags & PdfStringFlags.EncodingMask); } + set { _flags = (_flags & ~PdfStringFlags.EncodingMask) | ((PdfStringFlags)value & PdfStringFlags.EncodingMask); } + } + + /// + /// Gets a value indicating whether the string is a hexadecimal literal. + /// + public bool HexLiteral + { + get { return (_flags & PdfStringFlags.HexLiteral) != 0; } + set { _flags = value ? _flags | PdfStringFlags.HexLiteral : _flags & ~PdfStringFlags.HexLiteral; } + } + PdfStringFlags _flags; + + /// + /// Gets or sets the value as string + /// + public string Value + { + get { return _value ?? ""; } + set { _value = value ?? ""; } + } + string _value; + + /// + /// Gets or sets the string value for encryption purposes. + /// + internal byte[] EncryptionValue + { + // TODO: Unicode case is not handled! + get { return _value == null ? new byte[0] : PdfEncoders.RawEncoding.GetBytes(_value); } + set { _value = PdfEncoders.RawEncoding.GetString(value, 0, value.Length); } + } + + /// + /// Returns the string. + /// + public override string ToString() + { + return _value; + } + + /// + /// Writes the string literal with encoding DOCEncoded. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(new PdfString(_value, _flags)); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfUInteger.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfUInteger.cs new file mode 100644 index 00000000..60edf56c --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfUInteger.cs @@ -0,0 +1,228 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents a direct unsigned integer value. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfUInteger : PdfNumber, IConvertible + { + /// + /// Initializes a new instance of the class. + /// + public PdfUInteger() + { } + + /// + /// Initializes a new instance of the class. + /// + public PdfUInteger(uint value) + { + _value = value; + } + + /// + /// Gets the value as integer. + /// + public uint Value + { + // This class must behave like a value type. Therefore it cannot be changed (like System.String). + get { return _value; } + } + readonly uint _value; + + /// + /// Returns the unsigned integer as string. + /// + public override string ToString() + { + // ToString is impure but does not change the value of _value. + // ReSharper disable ImpureMethodCallOnReadonlyValueField + return _value.ToString(CultureInfo.InvariantCulture); + // ReSharper restore ImpureMethodCallOnReadonlyValueField + } + + /// + /// Writes the integer as string. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.Write(this); + } + + #region IConvertible Members + + /// + /// Converts the value of this instance to an equivalent 64-bit unsigned integer. + /// + public ulong ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(_value); + } + + /// + /// Converts the value of this instance to an equivalent 8-bit signed integer. + /// + public sbyte ToSByte(IFormatProvider provider) + { + throw new InvalidCastException(); + } + + /// + /// Converts the value of this instance to an equivalent double-precision floating-point number. + /// + public double ToDouble(IFormatProvider provider) + { + return _value; + } + + /// + /// Returns an undefined DateTime structure. + /// + public DateTime ToDateTime(IFormatProvider provider) + { + // TODO: Add PdfUInteger.ToDateTime implementation + return new DateTime(); + } + + /// + /// Converts the value of this instance to an equivalent single-precision floating-point number. + /// + public float ToSingle(IFormatProvider provider) + { + return _value; + } + + /// + /// Converts the value of this instance to an equivalent Boolean value. + /// + public bool ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(_value); + } + + /// + /// Converts the value of this instance to an equivalent 32-bit signed integer. + /// + public int ToInt32(IFormatProvider provider) + { + return Convert.ToInt32(_value); + } + + /// + /// Converts the value of this instance to an equivalent 16-bit unsigned integer. + /// + public ushort ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(_value); + } + + /// + /// Converts the value of this instance to an equivalent 16-bit signed integer. + /// + public short ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(_value); + } + + /// + /// Converts the value of this instance to an equivalent . + /// + string IConvertible.ToString(IFormatProvider provider) + { + return _value.ToString(provider); + } + + /// + /// Converts the value of this instance to an equivalent 8-bit unsigned integer. + /// + public byte ToByte(IFormatProvider provider) + { + return Convert.ToByte(_value); + } + + /// + /// Converts the value of this instance to an equivalent Unicode character. + /// + public char ToChar(IFormatProvider provider) + { + return Convert.ToChar(_value); + } + + /// + /// Converts the value of this instance to an equivalent 64-bit signed integer. + /// + public long ToInt64(IFormatProvider provider) + { + return _value; + } + + /// + /// Returns type code for 32-bit integers. + /// + public TypeCode GetTypeCode() + { + return TypeCode.Int32; + } + + /// + /// Converts the value of this instance to an equivalent number. + /// + public decimal ToDecimal(IFormatProvider provider) + { + return _value; + } + + /// + /// Returns null. + /// + public object ToType(Type conversionType, IFormatProvider provider) + { + // TODO: Add PdfUInteger.ToType implementation + return null; + } + + /// + /// Converts the value of this instance to an equivalent 32-bit unsigned integer. + /// + public uint ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(_value); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfUIntegerObject.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfUIntegerObject.cs new file mode 100644 index 00000000..11c9d729 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfUIntegerObject.cs @@ -0,0 +1,96 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using System.Globalization; +using PdfSharp.Pdf.IO; + +namespace PdfSharp.Pdf +{ + /// + /// Represents an indirect integer value. This type is not used by PDFsharp. If it is imported from + /// an external PDF file, the value is converted into a direct object. + /// + [DebuggerDisplay("({Value})")] + public sealed class PdfUIntegerObject : PdfNumberObject + { + /// + /// Initializes a new instance of the class. + /// + public PdfUIntegerObject() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + public PdfUIntegerObject(uint value) + { + _value = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The document. + /// The value. + public PdfUIntegerObject(PdfDocument document, uint value) + : base(document) + { + _value = value; + } + + /// + /// Gets the value as unsigned integer. + /// + public uint Value + { + get { return _value; } + } + readonly uint _value; + + /// + /// Returns the integer as string. + /// + public override string ToString() + { + return _value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// Writes the integer literal. + /// + internal override void WriteObject(PdfWriter writer) + { + writer.WriteBeginObject(this); + writer.Write(_value); + writer.WriteEndObject(); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/PdfViewerPreferences.cs b/src/PDFsharp/src/PdfSharp/Pdf/PdfViewerPreferences.cs new file mode 100644 index 00000000..20a9049a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/PdfViewerPreferences.cs @@ -0,0 +1,308 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Represents the PDF document viewer preferences dictionary. + /// + public sealed class PdfViewerPreferences : PdfDictionary + { + internal PdfViewerPreferences(PdfDocument document) + : base(document) + { } + + /// + /// Initializes a new instance of the class. + /// + PdfViewerPreferences(PdfDictionary dict) + : base(dict) + { } + + /// + /// Gets or sets a value indicating whether to hide the viewer applications tool + /// bars when the document is active. + /// + public bool HideToolbar + { + get { return Elements.GetBoolean(Keys.HideToolbar); } + set { Elements.SetBoolean(Keys.HideToolbar, value); } + } + + /// + /// Gets or sets a value indicating whether to hide the viewer applications + /// menu bar when the document is active. + /// + public bool HideMenubar + { + get { return Elements.GetBoolean(Keys.HideMenubar); } + set { Elements.SetBoolean(Keys.HideMenubar, value); } + } + + /// + /// Gets or sets a value indicating whether to hide user interface elements in + /// the documents window (such as scroll bars and navigation controls), + /// leaving only the documents contents displayed. + /// + public bool HideWindowUI + { + get { return Elements.GetBoolean(Keys.HideWindowUI); } + set { Elements.SetBoolean(Keys.HideWindowUI, value); } + } + + /// + /// Gets or sets a value indicating whether to resize the documents window to + /// fit the size of the first displayed page. + /// + public bool FitWindow + { + get { return Elements.GetBoolean(Keys.FitWindow); } + set { Elements.SetBoolean(Keys.FitWindow, value); } + } + + /// + /// Gets or sets a value indicating whether to position the documents window + /// in the center of the screen. + /// + public bool CenterWindow + { + get { return Elements.GetBoolean(Keys.CenterWindow); } + set { Elements.SetBoolean(Keys.CenterWindow, value); } + } + + /// + /// Gets or sets a value indicating whether the windows title bar + /// should display the document title taken from the Title entry of the document + /// information dictionary. If false, the title bar should instead display the name + /// of the PDF file containing the document. + /// + public bool DisplayDocTitle + { + get { return Elements.GetBoolean(Keys.DisplayDocTitle); } + set { Elements.SetBoolean(Keys.DisplayDocTitle, value); } + } + + /// + /// The predominant reading order for text: LeftToRight or RightToLeft + /// (including vertical writing systems, such as Chinese, Japanese, and Korean). + /// This entry has no direct effect on the documents contents or page numbering + /// but can be used to determine the relative positioning of pages when displayed + /// side by side or printed n-up. Default value: LeftToRight. + /// + public PdfReadingDirection? Direction + { + get + { + switch (Elements.GetName(Keys.Direction)) + { + case "L2R": + return PdfReadingDirection.LeftToRight; + + case "R2L": + return PdfReadingDirection.RightToLeft; + } + return null; + } + set + { + if (value.HasValue) + { + switch (value.Value) + { + case PdfReadingDirection.RightToLeft: + Elements.SetName(Keys.Direction, "R2L"); + break; + + default: + Elements.SetName(Keys.Direction, "L2R"); + break; + } + } + else + Elements.Remove(Keys.Direction); + } + } + + /// + /// Predefined keys of this dictionary. + /// + internal sealed class Keys : KeysBase + { + /// + /// (Optional) A flag specifying whether to hide the viewer applications tool + /// bars when the document is active. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string HideToolbar = "/HideToolbar"; + + /// + /// (Optional) A flag specifying whether to hide the viewer applications + /// menu bar when the document is active. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string HideMenubar = "/HideMenubar"; + + /// + /// (Optional) A flag specifying whether to hide user interface elements in + /// the documents window (such as scroll bars and navigation controls), + /// leaving only the documents contents displayed. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string HideWindowUI = "/HideWindowUI"; + + /// + /// (Optional) A flag specifying whether to resize the documents window to + /// fit the size of the first displayed page. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string FitWindow = "/FitWindow"; + + /// + /// (Optional) A flag specifying whether to position the documents window + /// in the center of the screen. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string CenterWindow = "/CenterWindow"; + + /// + /// (Optional; PDF 1.4) A flag specifying whether the windows title bar + /// should display the document title taken from the Title entry of the document + /// information dictionary. If false, the title bar should instead display the name + /// of the PDF file containing the document. Default value: false. + /// + [KeyInfo(KeyType.Boolean | KeyType.Optional)] + public const string DisplayDocTitle = "/DisplayDocTitle"; + + /// + /// (Optional) The documents page mode, specifying how to display the document on + /// exiting full-screen mode: + /// UseNone Neither document outline nor thumbnail images visible + /// UseOutlines Document outline visible + /// UseThumbs Thumbnail images visible + /// UseOC Optional content group panel visible + /// This entry is meaningful only if the value of the PageMode entry in the catalog + /// dictionary is FullScreen; it is ignored otherwise. Default value: UseNone. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string NonFullScreenPageMode = "/NonFullScreenPageMode"; + + /// + /// (Optional; PDF 1.3) The predominant reading order for text: + /// L2R Left to right + /// R2L Right to left (including vertical writing systems, such as Chinese, Japanese, and Korean) + /// This entry has no direct effect on the documents contents or page numbering + /// but can be used to determine the relative positioning of pages when displayed + /// side by side or printed n-up. Default value: L2R. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string Direction = "/Direction"; + + /// + /// (Optional; PDF 1.4) The name of the page boundary representing the area of a page + /// to be displayed when viewing the document on the screen. The value is the key + /// designating the relevant page boundary in the page object. If the specified page + /// boundary is not defined in the page object, its default value is used. + /// Default value: CropBox. + /// Note: This entry is intended primarily for use by prepress applications that + /// interpret or manipulate the page boundaries as described in Section 10.10.1, Page Boundaries. + /// Most PDF consumer applications disregard it. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string ViewArea = "/ViewArea"; + + /// + /// (Optional; PDF 1.4) The name of the page boundary to which the contents of a page + /// are to be clipped when viewing the document on the screen. The value is the key + /// designating the relevant page boundary in the page object. If the specified page + /// boundary is not defined in the page object, its default value is used. + /// Default value: CropBox. + /// Note: This entry is intended primarily for use by prepress applications that + /// interpret or manipulate the page boundaries as described in Section 10.10.1, Page Boundaries. + /// Most PDF consumer applications disregard it. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string ViewClip = "/ViewClip"; + + /// + /// (Optional; PDF 1.4) The name of the page boundary representing the area of a page + /// to be rendered when printing the document. The value is the key designating the + /// relevant page boundary in the page object. If the specified page boundary is not + /// defined in the page object, its default value is used. + /// Default value: CropBox. + /// Note: This entry is intended primarily for use by prepress applications that + /// interpret or manipulate the page boundaries as described in Section 10.10.1, Page Boundaries. + /// Most PDF consumer applications disregard it. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string PrintArea = "/PrintArea"; + + /// + /// (Optional; PDF 1.4) The name of the page boundary to which the contents of a page + /// are to be clipped when printing the document. The value is the key designating the + /// relevant page boundary in the page object. If the specified page boundary is not + /// defined in the page object, its default value is used. + /// Default value: CropBox. + /// Note: This entry is intended primarily for use by prepress applications that interpret + /// or manipulate the page boundaries. Most PDF consumer applications disregard it. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string PrintClip = "/PrintClip"; + + /// + /// (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is + /// displayed for this document. Valid values are None, which indicates that the print + /// dialog should reflect no page scaling, and AppDefault, which indicates that + /// applications should use the current print scaling. If this entry has an unrecognized + /// value, applications should use the current print scaling. + /// Default value: AppDefault. + /// Note: If the print dialog is suppressed and its parameters are provided directly + /// by the application, the value of this entry should still be used. + /// + [KeyInfo(KeyType.Name | KeyType.Optional)] + public const string PrintScaling = "/PrintScaling"; + + /// + /// Gets the KeysMeta for these keys. + /// + public static DictionaryMeta Meta + { + get { return _meta ?? (_meta = CreateMeta(typeof(Keys))); } + } + static DictionaryMeta _meta; + } + + /// + /// Gets the KeysMeta of this dictionary type. + /// + internal override DictionaryMeta Meta + { + get { return Keys.Meta; } + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/TrimMargins.cs b/src/PDFsharp/src/PdfSharp/Pdf/TrimMargins.cs new file mode 100644 index 00000000..0b2f26ea --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/TrimMargins.cs @@ -0,0 +1,116 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Diagnostics; +using PdfSharp.Drawing; + +namespace PdfSharp.Pdf +{ + /// + /// Represents trim margins added to the page. + /// + [DebuggerDisplay("(Left={_left.Millimeter}mm, Right={_right.Millimeter}mm, Top={_top.Millimeter}mm, Bottom={_bottom.Millimeter}mm)")] + public sealed class TrimMargins + { + ///// + ///// Clones this instance. + ///// + //public TrimMargins Clone() + //{ + // TrimMargins trimMargins = new TrimMargins(); + // trimMargins.left = left; + // trimMargins.top = top; + // trimMargins.right = right; + // trimMargins.bottom = bottom; + // return trimMargins; + //} + + /// + /// Sets all four crop margins simultaneously. + /// + public XUnit All + { + set + { + _left = value; + _right = value; + _top = value; + _bottom = value; + } + } + + /// + /// Gets or sets the left crop margin. + /// + public XUnit Left + { + get { return _left; } + set { _left = value; } + } + XUnit _left; + + /// + /// Gets or sets the right crop margin. + /// + public XUnit Right + { + get { return _right; } + set { _right = value; } + } + XUnit _right; + + /// + /// Gets or sets the top crop margin. + /// + public XUnit Top + { + get { return _top; } + set { _top = value; } + } + XUnit _top; + + /// + /// Gets or sets the bottom crop margin. + /// + public XUnit Bottom + { + get { return _bottom; } + set { _bottom = value; } + } + XUnit _bottom; + + /// + /// Gets a value indicating whether this instance has at least one margin with a value other than zero. + /// + public bool AreSet + { + get { return _left.Value != 0 || _right.Value != 0 || _top.Value != 0 || _bottom.Value != 0; } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/DocumentState.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/DocumentState.cs new file mode 100644 index 00000000..7d54e76a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/DocumentState.cs @@ -0,0 +1,55 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Identifies the state of the document + /// + [Flags] + enum DocumentState + { + /// + /// The document was created from scratch. + /// + Created = 0x0001, + + /// + /// The document was created by opening an existing PDF file. + /// + Imported = 0x0002, + + /// + /// The document is disposed. + /// + Disposed = 0x8000, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfColorMode.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfColorMode.cs new file mode 100644 index 00000000..cf2ba58b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfColorMode.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Specifies what color model is used in a PDF document. + /// + public enum PdfColorMode + { + /// + /// All color values are written as specified in the XColor objects they come from. + /// + Undefined, + + /// + /// All colors are converted to RGB. + /// + Rgb, + + /// + /// All colors are converted to CMYK. + /// + Cmyk, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfCustomValueCompression.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfCustomValueCompression.cs new file mode 100644 index 00000000..ff25f854 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfCustomValueCompression.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// This class is undocumented and may change or drop in future releases. + /// + public enum PdfCustomValueCompressionMode + { + /// + /// Use document default to determine compression. + /// + Default, + + /// + /// Leave custom values uncompressed. + /// + Uncompressed, + + /// + /// Compress custom values using FlateDecode. + /// + Compressed, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFlateEncodeMode.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFlateEncodeMode.cs new file mode 100644 index 00000000..edb78541 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFlateEncodeMode.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Sets the mode for the Deflater (FlateEncoder). + /// + public enum PdfFlateEncodeMode + { + /// + /// The default mode. + /// + Default, + + /// + /// Fast encoding, but larger PDF files. + /// + BestSpeed, + + /// + /// Best compression, but takes more time. + /// + BestCompression, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEmbedding.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEmbedding.cs new file mode 100644 index 00000000..d741204a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEmbedding.cs @@ -0,0 +1,63 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the embedding options of an XFont when converted into PDF. + /// Font embedding is not optional anymore. So Always is the only option. + /// + public enum PdfFontEmbedding + { + /// + /// All fonts are embedded. + /// + Always, + + /// + /// Fonts are not embedded. This is not an option anymore. + /// + [Obsolete("Fonts must always be embedded.")] + None, + + /// + /// Unicode fonts are embedded, WinAnsi fonts are not embedded. + /// + [Obsolete("Fonts must always be embedded.")] + Default, + + /// + /// Not yet implemented. + /// + [Obsolete("Fonts must always be embedded.")] + Automatic, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEncoding.cs new file mode 100644 index 00000000..ef9714ed --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfFontEncoding.cs @@ -0,0 +1,68 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the encoding schema used for an XFont when converted into PDF. + /// + public enum PdfFontEncoding + { + // TABLE + + /// + /// Cause a font to use Windows-1252 encoding to encode text rendered with this font. + /// Same as Windows1252 encoding. + /// + WinAnsi = 0, + + ///// + ///// Cause a font to use Windows-1252 (aka WinAnsi) encoding to encode text rendered with this font. + ///// + //Windows1252 = 0, + + /// + /// Cause a font to use Unicode encoding to encode text rendered with this font. + /// + Unicode = 1, + + /// + /// Unicode encoding. + /// + [Obsolete("Use WinAnsi or Unicode")] + Automatic = 1, // Force Unicode when used. + + // Implementation note: PdfFontEncoding uses incorrect terms. + // WinAnsi correspond to WinAnsiEncoding, while Unicode uses glyph indices. + // Furthermre the term WinAnsi is an oxymoron. + // Reference: TABLE D.1 Latin-text encodings / Page 996 + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfOutlineStyle.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfOutlineStyle.cs new file mode 100644 index 00000000..1da8f046 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfOutlineStyle.cs @@ -0,0 +1,62 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// Review: OK - StL/14-10-05 + +using System; + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the font style for the outline (bookmark) text. + /// + [Flags] + public enum PdfOutlineStyle // Reference: TABLE 8.5 Ouline Item flags / Page 587 + { + /// + /// Outline text is displayed using a regular font. + /// + Regular = 0, + + /// + /// Outline text is displayed using an italic font. + /// + Italic = 1, + + /// + /// Outline text is displayed using a bold font. + /// + Bold = 2, + + /// + /// Outline text is displayed using a bold and italic font. + /// + BoldItalic = 3, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageDestinationType.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageDestinationType.cs new file mode 100644 index 00000000..adda1163 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageDestinationType.cs @@ -0,0 +1,98 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming because we use PDF names. + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the type of a page destination in outline items, annotations, or actions.. + /// + public enum PdfPageDestinationType // Reference: TABLE 8.2 Destination Syntax / Page 582 + { + // Except for FitR the documentation text is outdated. + + /// + /// Display the page with the coordinates (left, top) positioned at the upper-left corner of + /// the window and the contents of the page magnified by the factor zoom. + /// + Xyz, + + /// + /// Display the page with its contents magnified just enough to fit the + /// entire page within the window both horizontally and vertically. + /// + Fit, + + /// + /// Display the page with the vertical coordinate top positioned at the top edge of + /// the window and the contents of the page magnified just enough to fit the entire + /// width of the page within the window. + /// + FitH, + + /// + /// Display the page with the horizontal coordinate left positioned at the left edge of + /// the window and the contents of the page magnified just enough to fit the entire + /// height of the page within the window. + /// + FitV, + + /// + /// Display the page designated by page, with its contents magnified just enough to + /// fit the rectangle specified by the coordinates left, bottom, right, and topentirely + /// within the window both horizontally and vertically. If the required horizontal and + /// vertical magnification factors are different, use the smaller of the two, centering + /// the rectangle within the window in the other dimension. A null value for any of + /// the parameters may result in unpredictable behavior. + /// + FitR, + + /// + /// Display the page with its contents magnified just enough to fit the rectangle specified + /// by the coordinates left, bottom, right, and topentirely within the window both + /// horizontally and vertically. + /// + FitB, + + /// + /// Display the page with the vertical coordinate top positioned at the top edge of + /// the window and the contents of the page magnified just enough to fit the entire + /// width of its bounding box within the window. + /// + FitBH, + + /// + /// Display the page with the horizontal coordinate left positioned at the left edge of + /// the window and the contents of the page magnified just enough to fit the entire + /// height of its bounding box within the window. + /// + FitBV, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageLayout.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageLayout.cs new file mode 100644 index 00000000..50485254 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageLayout.cs @@ -0,0 +1,67 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Specifies the page layout to be used by a viewer when the document is opened. + /// + public enum PdfPageLayout + { + /// + /// Display one page at a time. + /// + SinglePage, + + /// + /// Display the pages in one column. + /// + OneColumn, + + /// + /// Display the pages in two columns, with oddnumbered pages on the left. + /// + TwoColumnLeft, + + /// + /// Display the pages in two columns, with oddnumbered pages on the right. + /// + TwoColumnRight, + + /// + /// (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left. + /// + TwoPageLeft, + + /// + /// (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right. + /// + TwoPageRight, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageMode.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageMode.cs new file mode 100644 index 00000000..c6c6b324 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfPageMode.cs @@ -0,0 +1,69 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Pdf +{ + /// + /// Specifies how the document should be displayed by a viewer when opened. + /// + public enum PdfPageMode + { + /// + /// Neither document outline nor thumbnail images visible. + /// + UseNone, + + /// + /// Document outline visible. + /// + UseOutlines, + + /// + /// Thumbnail images visible. + /// + UseThumbs, + + /// + /// Full-screen mode, with no menu bar, windowcontrols, or any other window visible. + /// + FullScreen, + + /// + /// (PDF 1.5) Optional content group panel visible. + /// + UseOC, + + /// + /// (PDF 1.6) Attachments panel visible. + /// + UseAttachments, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfReadingDirection.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfReadingDirection.cs new file mode 100644 index 00000000..75d448e6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfReadingDirection.cs @@ -0,0 +1,47 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Specifies how the document should be displayed by a viewer when opened. + /// + public enum PdfReadingDirection + { + /// + /// Left to right. + /// + LeftToRight, + + /// + /// Right to left (including vertical writing systems, such as Chinese, Japanese, and Korean) + /// + RightToLeft, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfTextStringEncoding.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfTextStringEncoding.cs new file mode 100644 index 00000000..ce519bc2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfTextStringEncoding.cs @@ -0,0 +1,50 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +// ReSharper disable InconsistentNaming + +namespace PdfSharp.Pdf +{ + /// + /// Specifies how text strings are encoded. A text string is any text used outside of a page content + /// stream, e.g. document information, outline text, annotation text etc. + /// + public enum PdfTextStringEncoding + { + /// + /// Specifies that hypertext uses PDF DocEncoding. + /// + PDFDocEncoding = 0, + + /// + /// Specifies that hypertext uses unicode encoding. + /// + Unicode = 1, + } +} diff --git a/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfUseFlateDecoderForJpegImages.cs b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfUseFlateDecoderForJpegImages.cs new file mode 100644 index 00000000..2cd8b249 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Pdf/enums/PdfUseFlateDecoderForJpegImages.cs @@ -0,0 +1,55 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Pdf +{ + /// + /// Specifies whether to compress JPEG images with the FlateDecode filter. + /// + public enum PdfUseFlateDecoderForJpegImages + { + /// + /// PDFsharp will try FlateDecode and use it if it leads to a reduction in PDF file size. + /// When FlateEncodeMode is set to BestCompression, this is more likely to reduce the file size, + /// but it takes considerably more time to create the PDF file. + /// + Automatic, + + /// + /// PDFsharp will never use FlateDecode - files may be a few bytes larger, but file creation is faster. + /// + Never, + + /// + /// PDFsharp will always use FlateDecode, even if this leads to larger files; + /// this option is meant for testing purposes only and should not be used for production code. + /// + Always, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/PdfSharp.csproj b/src/PDFsharp/src/PdfSharp/PdfSharp.csproj new file mode 100644 index 00000000..cdae9caf --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/PdfSharp.csproj @@ -0,0 +1,435 @@ + + + + + Debug + AnyCPU + {5A6055BC-BF86-4FDD-9F62-0109DB7A303B} + Library + Properties + PdfSharp + PdfSharp + v3.5 + 512 + SAK + SAK + SAK + SAK + + + + true + full + false + bin\Debug\ + TRACE;DEBUG;CORE;CORE_WITH_GDI + prompt + 4 + false + true + false + + + pdbonly + true + bin\Release\ + TRACE;CORE;CORE_WITH_GDI + prompt + 4 + false + bin\Release\PdfSharp.xml + + + true + + + StrongnameKey.snk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ResGen + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Properties/AssemblyInfo.cs b/src/PDFsharp/src/PdfSharp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..37640488 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,72 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if WPF +using System.Windows.Markup; +#endif + +[assembly: AssemblyTitle(PdfSharp.VersionInfo.Title)] +[assembly: AssemblyVersion(PdfSharp.VersionInfo.Version)] +[assembly: AssemblyDescription(PdfSharp.VersionInfo.Description)] +[assembly: AssemblyConfiguration(PdfSharp.VersionInfo.Configuration)] +[assembly: AssemblyCompany(PdfSharp.VersionInfo.Company)] +#if DEBUG +[assembly: AssemblyProduct(PdfSharp.ProductVersionInfo.Product + " (Debug Build)")] +#else + [assembly: AssemblyProduct(PdfSharp.ProductVersionInfo.Product)] +#endif +[assembly: AssemblyCopyright(PdfSharp.VersionInfo.Copyright)] +[assembly: AssemblyTrademark(PdfSharp.VersionInfo.Trademark)] +[assembly: AssemblyCulture(PdfSharp.VersionInfo.Culture)] + +[assembly: NeutralResourcesLanguage("en-US")] + +#if WPF +[assembly: XmlnsDefinition("http://schemas.empira.com/pdfsharp/2010/xaml/presentation", "PdfSharp.Windows")] +#endif + +[assembly: InternalsVisibleTo("PdfSharp.UnitTest, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] +#if WPF +[assembly: InternalsVisibleTo("PdfSharp.Xps, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] +[assembly: InternalsVisibleTo("Edf.Xps, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] +#endif +[assembly: InternalsVisibleTo("PdfSharp.Toolkit.Silverlight, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] + +[assembly: InternalsVisibleTo("ConsoleApplication-GDI, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] +[assembly: InternalsVisibleTo("ConsoleApplication-Core, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] + + +[assembly: InternalsVisibleTo("PdfSharp.UA, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] +[assembly: InternalsVisibleTo("PdfSharp.UA-wpf, PublicKey=00240000048000009400000006020000002400005253413100040000010001008794e803e566eccc3c9181f52c4f7044e5442cc2ce3cbba9fc11bc4186ba2e446cd31deea20c1a8f499e978417fad2bc74143a4f8398f7cf5c5c0271b0f7fe907c537cff28b9d582da41289d1dae90168a3da2a5ed1115210a18fdae832479d3e639ca4003286ba8b98dc9144615c040ed838981ac816112df3b5a9e7cab4fbb")] + +[assembly: ComVisible(false)] diff --git a/src/PDFsharp/src/PdfSharp/Resources/Messages.de.restext b/src/PDFsharp/src/PdfSharp/Resources/Messages.de.restext new file mode 100644 index 00000000..c463242b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Resources/Messages.de.restext @@ -0,0 +1,20 @@ +; PDFsharp string resources (German) +; +; Must be saved as Unicode (UTF-8 with signature) to force resgen.exe to process German umlauts. + +; ----- General Messages -------------------------------------------------------------------------- + +SampleMessage1 = Das ist Beispielnachricht 1 (de). +SampleMessage2 = Das ist Beispielnachricht 2: {0}. + +; ----- XGraphics Messages ------------------------------------------------------------------------ + +; ----- Pdf Messages ------------------------------------------------------------------------------ + +NameMustStartWithSlash = Ein PDF-Name muss mit einem Schrägstrich ('/') beginnen. +UserOrOwnerPasswordRequired = Zum Verschlüsseln des Dokuments muss ein Kennwort zum Öffnen (UserPassword) oder ein Berechtigungskennwort (OwnerPassword) gesetzt sein. + +; ----- PdfParser Messages ------------------------------------------------------------------------ + +UnexpectedToken = Token '{0}' wird an dieser Stelle nicht erwartet. +UnknownEncryption = Das PDF-Dokument ist mit einer von PDFsharp nicht unterstützten Verschlüsselung geschützt. diff --git a/src/PDFsharp/src/PdfSharp/Resources/Messages.restext b/src/PDFsharp/src/PdfSharp/Resources/Messages.restext new file mode 100644 index 00000000..e47d27ff --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Resources/Messages.restext @@ -0,0 +1,20 @@ +; PDFsharp string resources (English) +; +; + +; ----- General Messages -------------------------------------------------------------------------- + +SampleMessage1 = This is sample message 1 (2.0). +SampleMessage2 = This is sample message 2: {0}. + +; ----- XGraphics Messages ------------------------------------------------------------------------ + +; ----- Pdf Messages ------------------------------------------------------------------------------ + +NameMustStartWithSlash = A PDF name must start with a slash ('/'). +UserOrOwnerPasswordRequired = At least a user or an owner password is required to encrypt the document. + +; ----- PdfParser Messages ------------------------------------------------------------------------ + +UnexpectedToken = Token '{0}' was not expected. +UnknownEncryption = The PDF document is protected with an encryption not supported by PDFsharp. diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/Adler32.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/Adler32.cs new file mode 100644 index 00000000..dd462469 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/Adler32.cs @@ -0,0 +1,245 @@ +// Adler32.cs - Computes Adler32 data checksum of a data stream +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Checksums +{ + + /// + /// Computes Adler32 checksum for a stream of data. An Adler32 + /// checksum is not as reliable as a CRC32 checksum, but a lot faster to + /// compute. + /// + /// The specification for Adler32 may be found in RFC 1950. + /// ZLIB Compressed Data Format Specification version 3.3) + /// + /// + /// From that document: + /// + /// "ADLER32 (Adler-32 checksum) + /// This contains a checksum value of the uncompressed data + /// (excluding any dictionary data) computed according to Adler-32 + /// algorithm. This algorithm is a 32-bit extension and improvement + /// of the Fletcher algorithm, used in the ITU-T X.224 / ISO 8073 + /// standard. + /// + /// Adler-32 is composed of two sums accumulated per byte: s1 is + /// the sum of all bytes, s2 is the sum of all s1 values. Both sums + /// are done modulo 65521. s1 is initialized to 1, s2 to zero. The + /// Adler-32 checksum is stored as s2*65536 + s1 in most- + /// significant-byte first (network) order." + /// + /// "8.2. The Adler-32 algorithm + /// + /// The Adler-32 algorithm is much faster than the CRC32 algorithm yet + /// still provides an extremely low probability of undetected errors. + /// + /// The modulo on unsigned long accumulators can be delayed for 5552 + /// bytes, so the modulo operation time is negligible. If the bytes + /// are a, b, c, the second sum is 3a + 2b + c + 3, and so is position + /// and order sensitive, unlike the first sum, which is just a + /// checksum. That 65521 is prime is important to avoid a possible + /// large class of two-byte errors that leave the check unchanged. + /// (The Fletcher checksum uses 255, which is not prime and which also + /// makes the Fletcher check insensitive to single byte changes 0 - + /// 255.) + /// + /// The sum s1 is initialized to 1 instead of zero to make the length + /// of the sequence part of s2, so that the length does not have to be + /// checked separately. (Any sequence of zeroes has a Fletcher + /// checksum of zero.)" + /// + /// + /// + internal sealed class Adler32 : IChecksum + { + /// + /// largest prime smaller than 65536 + /// + const uint BASE = 65521; + + /// + /// Returns the Adler32 data checksum computed so far. + /// + public long Value + { + get + { + return checksum; + } + } + + /// + /// Creates a new instance of the Adler32 class. + /// The checksum starts off with a value of 1. + /// + public Adler32() + { + Reset(); + } + + /// + /// Resets the Adler32 checksum to the initial value. + /// + public void Reset() + { + checksum = 1; + } + + /// + /// Updates the checksum with a byte value. + /// + /// + /// The data value to add. The high byte of the int is ignored. + /// + public void Update(int value) + { + // We could make a length 1 byte array and call update again, but I + // would rather not have that overhead + uint s1 = checksum & 0xFFFF; + uint s2 = checksum >> 16; + + s1 = (s1 + ((uint)value & 0xFF)) % BASE; + s2 = (s1 + s2) % BASE; + + checksum = (s2 << 16) + s1; + } + + /// + /// Updates the checksum with an array of bytes. + /// + /// + /// The source of the data to update with. + /// + public void Update(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + Update(buffer, 0, buffer.Length); + } + + /// + /// Updates the checksum with the bytes taken from the array. + /// + /// + /// an array of bytes + /// + /// + /// the start of the data used for this update + /// + /// + /// the number of bytes to use for this update + /// + public void Update(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (offset < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("offset"); +#else + throw new ArgumentOutOfRangeException("offset", "cannot be negative"); +#endif + } + + if (count < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("count"); +#else + throw new ArgumentOutOfRangeException("count", "cannot be negative"); +#endif + } + + if (offset >= buffer.Length) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("offset"); +#else + throw new ArgumentOutOfRangeException("offset", "not a valid index into buffer"); +#endif + } + + if (offset + count > buffer.Length) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("count"); +#else + throw new ArgumentOutOfRangeException("count", "exceeds buffer size"); +#endif + } + + //(By Per Bothner) + uint s1 = checksum & 0xFFFF; + uint s2 = checksum >> 16; + + while (count > 0) + { + // We can defer the modulo operation: + // s1 maximally grows from 65521 to 65521 + 255 * 3800 + // s2 maximally grows by 3800 * median(s1) = 2090079800 < 2^31 + int n = 3800; + if (n > count) + { + n = count; + } + count -= n; + while (--n >= 0) + { + s1 = s1 + (uint)(buffer[offset++] & 0xff); + s2 = s2 + s1; + } + s1 %= BASE; + s2 %= BASE; + } + + checksum = (s2 << 16) | s1; + } + + #region Instance Fields + uint checksum; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/CRC32.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/CRC32.cs new file mode 100644 index 00000000..584b5c66 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/CRC32.cs @@ -0,0 +1,231 @@ +// CRC32.cs - Computes CRC32 data checksum of a data stream +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Checksums +{ + + /// + /// Generate a table for a byte-wise 32-bit CRC calculation on the polynomial: + /// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + /// + /// Polynomials over GF(2) are represented in binary, one bit per coefficient, + /// with the lowest powers in the most significant bit. Then adding polynomials + /// is just exclusive-or, and multiplying a polynomial by x is a right shift by + /// one. If we call the above polynomial p, and represent a byte as the + /// polynomial q, also with the lowest power in the most significant bit (so the + /// byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + /// where a mod b means the remainder after dividing a by b. + /// + /// This calculation is done using the shift-register method of multiplying and + /// taking the remainder. The register is initialized to zero, and for each + /// incoming bit, x^32 is added mod p to the register if the bit is a one (where + /// x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + /// x (which is shifting right by one and adding x^32 mod p if the bit shifted + /// out is a one). We start with the highest power (least significant bit) of + /// q and repeat for all eight bits of q. + /// + /// The table is simply the CRC of all possible eight bit values. This is all + /// the information needed to generate CRC's on data a byte at a time for all + /// combinations of CRC register values and incoming bytes. + /// + internal sealed class Crc32 : IChecksum + { + const uint CrcSeed = 0xFFFFFFFF; + + readonly static uint[] CrcTable = new uint[] { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, + 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, + 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, + 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, + 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, + 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, + 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, + 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, + 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, + 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, + 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, + 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, + 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, + 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, + 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, + 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, + 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, + 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, + 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, + 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, + 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, + 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, + 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, + 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, + 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, + 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, + 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, + 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, + 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, + 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, + 0x2D02EF8D + }; + + internal static uint ComputeCrc32(uint oldCrc, byte value) + { + return (uint)(Crc32.CrcTable[(oldCrc ^ value) & 0xFF] ^ (oldCrc >> 8)); + } + + /// + /// The crc data checksum so far. + /// + uint crc; + + /// + /// Returns the CRC32 data checksum computed so far. + /// + public long Value + { + get + { + return (long)crc; + } + set + { + crc = (uint)value; + } + } + + /// + /// Resets the CRC32 data checksum as if no update was ever called. + /// + public void Reset() + { + crc = 0; + } + + /// + /// Updates the checksum with the int bval. + /// + /// + /// the byte is taken as the lower 8 bits of value + /// + public void Update(int value) + { + crc ^= CrcSeed; + crc = CrcTable[(crc ^ value) & 0xFF] ^ (crc >> 8); + crc ^= CrcSeed; + } + + /// + /// Updates the checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + public void Update(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + Update(buffer, 0, buffer.Length); + } + + /// + /// Adds the byte array to the data checksum. + /// + /// + /// The buffer which contains the data + /// + /// + /// The offset in the buffer where the data starts + /// + /// + /// The number of data bytes to update the CRC with. + /// + public void Update(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (count < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("count"); +#else + throw new ArgumentOutOfRangeException("count", "Count cannot be less than zero"); +#endif + } + + if (offset < 0 || offset + count > buffer.Length) + { + throw new ArgumentOutOfRangeException("offset"); + } + + crc ^= CrcSeed; + + while (--count >= 0) + { + crc = CrcTable[(crc ^ buffer[offset++]) & 0xFF] ^ (crc >> 8); + } + + crc ^= CrcSeed; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/IChecksum.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/IChecksum.cs new file mode 100644 index 00000000..bcf34f84 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Checksums/IChecksum.cs @@ -0,0 +1,93 @@ +// IChecksum.cs - Interface to compute a data checksum +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +namespace PdfSharp.SharpZipLib.Checksums +{ + + /// + /// Interface to compute a data checksum used by checked input/output streams. + /// A data checksum can be updated by one byte or with a byte array. After each + /// update the value of the current checksum can be returned by calling + /// getValue. The complete checksum object can also be reset + /// so it can be used again with new data. + /// + internal interface IChecksum + { + /// + /// Returns the data checksum computed so far. + /// + long Value + { + get; + } + + /// + /// Resets the data checksum as if no update was ever called. + /// + void Reset(); + + /// + /// Adds one byte to the data checksum. + /// + /// + /// the data value to add. The high byte of the int is ignored. + /// + void Update(int value); + + /// + /// Updates the data checksum with the bytes taken from the array. + /// + /// + /// buffer an array of bytes + /// + void Update(byte[] buffer); + + /// + /// Adds the byte array to the data checksum. + /// + /// + /// The buffer which contains the data + /// + /// + /// The offset in the buffer where the data starts + /// + /// + /// the number of data bytes to add. + /// + void Update(byte[] buffer, int offset, int count); + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/ReadMe.txt b/src/PDFsharp/src/PdfSharp/SharpZipLib/ReadMe.txt new file mode 100644 index 00000000..ac076efc --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/ReadMe.txt @@ -0,0 +1,3 @@ + +This code is an excerpt from the SharpZipLib. The code is unmodified except that all classes +are made internal and moved to the namespace PdfSharp.SharpZipLib. diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/SharpZipBaseException.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/SharpZipBaseException.cs new file mode 100644 index 00000000..9f7425ef --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/SharpZipBaseException.cs @@ -0,0 +1,99 @@ +// SharpZipBaseException.cs +// +// Copyright 2004 John Reilly +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +#if !NETCF_1_0 && !NETCF_2_0 && !DNC10 +using System.Runtime.Serialization; +#endif + +namespace PdfSharp.SharpZipLib +{ + /// + /// SharpZipBaseException is the base exception class for the SharpZipLibrary. + /// All library exceptions are derived from this. + /// + /// NOTE: Not all exceptions thrown will be derived from this class. + /// A variety of other exceptions are possible for example +#if !NETCF_1_0 && !NETCF_2_0 && !DNC10 + [Serializable] +#endif + internal class SharpZipBaseException : +#if NETFX_CORE || DNC10 + Exception +#else + ApplicationException +#endif + { +#if false//!NETCF_1_0 && !NETCF_2_0 + /// + /// Deserialization constructor + /// + /// for this constructor + /// for this constructor + protected SharpZipBaseException(SerializationInfo info, StreamingContext context ) + : base( info, context ) + { + } +#endif + + /// + /// Initializes a new instance of the SharpZipBaseException class. + /// + public SharpZipBaseException() + { + } + + /// + /// Initializes a new instance of the SharpZipBaseException class with a specified error message. + /// + /// A message describing the exception. + public SharpZipBaseException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the SharpZipBaseException class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// A message describing the exception. + /// The inner exception + public SharpZipBaseException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Deflater.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Deflater.cs new file mode 100644 index 00000000..cf6ed8ac --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Deflater.cs @@ -0,0 +1,595 @@ +// Deflater.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// This is the Deflater class. The deflater class compresses input + /// with the deflate algorithm described in RFC 1951. It has several + /// compression levels and three different strategies described below. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of deflate and setInput. + /// + /// Author of the original java version: Jochen Hoenicke + /// + internal class Deflater + { + #region Deflater Documentation + /* + * The Deflater can do the following state transitions: + * + * (1) -> INIT_STATE ----> INIT_FINISHING_STATE ---. + * / | (2) (5) | + * / v (5) | + * (3)| SETDICT_STATE ---> SETDICT_FINISHING_STATE |(3) + * \ | (3) | ,--------' + * | | | (3) / + * v v (5) v v + * (1) -> BUSY_STATE ----> FINISHING_STATE + * | (6) + * v + * FINISHED_STATE + * \_____________________________________/ + * | (7) + * v + * CLOSED_STATE + * + * (1) If we should produce a header we start in INIT_STATE, otherwise + * we start in BUSY_STATE. + * (2) A dictionary may be set only when we are in INIT_STATE, then + * we change the state as indicated. + * (3) Whether a dictionary is set or not, on the first call of deflate + * we change to BUSY_STATE. + * (4) -- intentionally left blank -- :) + * (5) FINISHING_STATE is entered, when flush() is called to indicate that + * there is no more INPUT. There are also states indicating, that + * the header wasn't written yet. + * (6) FINISHED_STATE is entered, when everything has been flushed to the + * internal pending output buffer. + * (7) At any time (7) + * + */ + #endregion + #region Public Constants + /// + /// The best and slowest compression level. This tries to find very + /// long and distant string repetitions. + /// + public const int BEST_COMPRESSION = 9; + + /// + /// The worst but fastest compression level. + /// + public const int BEST_SPEED = 1; + + /// + /// The default compression level. + /// + public const int DEFAULT_COMPRESSION = -1; + + /// + /// This level won't compress at all but output uncompressed blocks. + /// + public const int NO_COMPRESSION = 0; + + /// + /// The compression method. This is the only method supported so far. + /// There is no need to use this constant at all. + /// + public const int DEFLATED = 8; + #endregion + #region Local Constants + private const int IS_SETDICT = 0x01; + private const int IS_FLUSHING = 0x04; + private const int IS_FINISHING = 0x08; + + private const int INIT_STATE = 0x00; + private const int SETDICT_STATE = 0x01; + // private static int INIT_FINISHING_STATE = 0x08; + // private static int SETDICT_FINISHING_STATE = 0x09; + private const int BUSY_STATE = 0x10; + private const int FLUSHING_STATE = 0x14; + private const int FINISHING_STATE = 0x1c; + private const int FINISHED_STATE = 0x1e; + private const int CLOSED_STATE = 0x7f; + #endregion + #region Constructors + /// + /// Creates a new deflater with default compression level. + /// + public Deflater() + : this(DEFAULT_COMPRESSION, false) + { + + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION, or DEFAULT_COMPRESSION. + /// + /// if lvl is out of range. + public Deflater(int level) + : this(level, false) + { + + } + + /// + /// Creates a new deflater with given compression level. + /// + /// + /// the compression level, a value between NO_COMPRESSION + /// and BEST_COMPRESSION. + /// + /// + /// true, if we should suppress the Zlib/RFC1950 header at the + /// beginning and the adler checksum at the end of the output. This is + /// useful for the GZIP/PKZIP formats. + /// + /// if lvl is out of range. + public Deflater(int level, bool noZlibHeaderOrFooter) + { + if (level == DEFAULT_COMPRESSION) + { + level = 6; + } + else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + { + throw new ArgumentOutOfRangeException("level"); + } + + pending = new DeflaterPending(); + engine = new DeflaterEngine(pending); + this.noZlibHeaderOrFooter = noZlibHeaderOrFooter; + SetStrategy(DeflateStrategy.Default); + SetLevel(level); + Reset(); + } + #endregion + + /// + /// Resets the deflater. The deflater acts afterwards as if it was + /// just created with the same compression level and strategy as it + /// had before. + /// + public void Reset() + { + state = (noZlibHeaderOrFooter ? BUSY_STATE : INIT_STATE); + totalOut = 0; + pending.Reset(); + engine.Reset(); + } + + /// + /// Gets the current adler checksum of the data that was processed so far. + /// + public int Adler + { + get + { + return engine.Adler; + } + } + + /// + /// Gets the number of input bytes processed so far. + /// + public long TotalIn + { + get + { + return engine.TotalIn; + } + } + + /// + /// Gets the number of output bytes so far. + /// + public long TotalOut + { + get + { + return totalOut; + } + } + + /// + /// Flushes the current input block. Further calls to deflate() will + /// produce enough output to inflate everything in the current input + /// block. This is not part of Sun's JDK so I have made it package + /// private. It is used by DeflaterOutputStream to implement + /// flush(). + /// + public void Flush() + { + state |= IS_FLUSHING; + } + + /// + /// Finishes the deflater with the current input block. It is an error + /// to give more input after this method was called. This method must + /// be called to force all bytes to be flushed. + /// + public void Finish() + { + state |= (IS_FLUSHING | IS_FINISHING); + } + + /// + /// Returns true if the stream was finished and no more output bytes + /// are available. + /// + public bool IsFinished + { + get + { + return (state == FINISHED_STATE) && pending.IsFlushed; + } + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method can also return true when the stream + /// was finished. + /// + public bool IsNeedingInput + { + get + { + return engine.NeedsInput(); + } + } + + /// + /// Sets the data which should be compressed next. This should be only + /// called when needsInput indicates that more input is needed. + /// If you call setInput when needsInput() returns false, the + /// previous input that is still pending will be thrown away. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// This call is equivalent to setInput(input, 0, input.length). + /// + /// + /// the buffer containing the input data. + /// + /// + /// if the buffer was finished() or ended(). + /// + public void SetInput(byte[] input) + { + SetInput(input, 0, input.Length); + } + + /// + /// Sets the data which should be compressed next. This should be + /// only called when needsInput indicates that more input is needed. + /// The given byte array should not be changed, before needsInput() returns + /// true again. + /// + /// + /// the buffer containing the input data. + /// + /// + /// the start of the data. + /// + /// + /// the number of data bytes of input. + /// + /// + /// if the buffer was Finish()ed or if previous input is still pending. + /// + public void SetInput(byte[] input, int offset, int count) + { + if ((state & IS_FINISHING) != 0) + { + throw new InvalidOperationException("Finish() already called"); + } + engine.SetInput(input, offset, count); + } + + /// + /// Sets the compression level. There is no guarantee of the exact + /// position of the change, but if you call this when needsInput is + /// true the change of compression level will occur somewhere near + /// before the end of the so far given input. + /// + /// + /// the new compression level. + /// + public void SetLevel(int level) + { + if (level == DEFAULT_COMPRESSION) + { + level = 6; + } + else if (level < NO_COMPRESSION || level > BEST_COMPRESSION) + { + throw new ArgumentOutOfRangeException("level"); + } + + if (this.level != level) + { + this.level = level; + engine.SetLevel(level); + } + } + + /// + /// Get current compression level + /// + /// Returns the current compression level + public int GetLevel() + { + return level; + } + + /// + /// Sets the compression strategy. Strategy is one of + /// DEFAULT_STRATEGY, HUFFMAN_ONLY and FILTERED. For the exact + /// position where the strategy is changed, the same as for + /// SetLevel() applies. + /// + /// + /// The new compression strategy. + /// + public void SetStrategy(DeflateStrategy strategy) + { + engine.Strategy = strategy; + } + + /// + /// Deflates the current input block with to the given array. + /// + /// + /// The buffer where compressed data is stored + /// + /// + /// The number of compressed bytes added to the output, or 0 if either + /// IsNeedingInput() or IsFinished returns true or length is zero. + /// + public int Deflate(byte[] output) + { + return Deflate(output, 0, output.Length); + } + + /// + /// Deflates the current input block to the given array. + /// + /// + /// Buffer to store the compressed data. + /// + /// + /// Offset into the output array. + /// + /// + /// The maximum number of bytes that may be stored. + /// + /// + /// The number of compressed bytes added to the output, or 0 if either + /// needsInput() or finished() returns true or length is zero. + /// + /// + /// If Finish() was previously called. + /// + /// + /// If offset or length don't match the array length. + /// + public int Deflate(byte[] output, int offset, int length) + { + int origLength = length; + + if (state == CLOSED_STATE) + { + throw new InvalidOperationException("Deflater closed"); + } + + if (state < BUSY_STATE) + { + // output header + int header = (DEFLATED + + ((DeflaterConstants.MAX_WBITS - 8) << 4)) << 8; + int level_flags = (level - 1) >> 1; + if (level_flags < 0 || level_flags > 3) + { + level_flags = 3; + } + header |= level_flags << 6; + if ((state & IS_SETDICT) != 0) + { + // Dictionary was set + header |= DeflaterConstants.PRESET_DICT; + } + header += 31 - (header % 31); + + pending.WriteShortMSB(header); + if ((state & IS_SETDICT) != 0) + { + int chksum = engine.Adler; + engine.ResetAdler(); + pending.WriteShortMSB(chksum >> 16); + pending.WriteShortMSB(chksum & 0xffff); + } + + state = BUSY_STATE | (state & (IS_FLUSHING | IS_FINISHING)); + } + + for (; ; ) + { + int count = pending.Flush(output, offset, length); + offset += count; + totalOut += count; + length -= count; + + if (length == 0 || state == FINISHED_STATE) + { + break; + } + + if (!engine.Deflate((state & IS_FLUSHING) != 0, (state & IS_FINISHING) != 0)) + { + if (state == BUSY_STATE) + { + // We need more input now + return origLength - length; + } + else if (state == FLUSHING_STATE) + { + if (level != NO_COMPRESSION) + { + /* We have to supply some lookahead. 8 bit lookahead + * is needed by the zlib inflater, and we must fill + * the next byte, so that all bits are flushed. + */ + int neededbits = 8 + ((-pending.BitCount) & 7); + while (neededbits > 0) + { + /* write a static tree block consisting solely of + * an EOF: + */ + pending.WriteBits(2, 10); + neededbits -= 10; + } + } + state = BUSY_STATE; + } + else if (state == FINISHING_STATE) + { + pending.AlignToByte(); + + // Compressed data is complete. Write footer information if required. + if (!noZlibHeaderOrFooter) + { + int adler = engine.Adler; + pending.WriteShortMSB(adler >> 16); + pending.WriteShortMSB(adler & 0xffff); + } + state = FINISHED_STATE; + } + } + } + return origLength - length; + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// This call is equivalent to setDictionary(dict, 0, dict.Length). + /// + /// + /// the dictionary. + /// + /// + /// if SetInput () or Deflate () were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dictionary) + { + SetDictionary(dictionary, 0, dictionary.Length); + } + + /// + /// Sets the dictionary which should be used in the deflate process. + /// The dictionary is a byte array containing strings that are + /// likely to occur in the data which should be compressed. The + /// dictionary is not stored in the compressed output, only a + /// checksum. To decompress the output you need to supply the same + /// dictionary again. + /// + /// + /// The dictionary data + /// + /// + /// The index where dictionary information commences. + /// + /// + /// The number of bytes in the dictionary. + /// + /// + /// If SetInput () or Deflate() were already called or another dictionary was already set. + /// + public void SetDictionary(byte[] dictionary, int index, int count) + { + if (state != INIT_STATE) + { + throw new InvalidOperationException(); + } + + state = SETDICT_STATE; + engine.SetDictionary(dictionary, index, count); + } + + #region Instance Fields + /// + /// Compression level. + /// + int level; + + /// + /// If true no Zlib/RFC1950 headers or footers are generated + /// + bool noZlibHeaderOrFooter; + + /// + /// The current state. + /// + int state; + + /// + /// The total bytes of output written. + /// + long totalOut; + + /// + /// The pending output. + /// + DeflaterPending pending; + + /// + /// The deflater engine. + /// + DeflaterEngine engine; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterConstants.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterConstants.cs new file mode 100644 index 00000000..f638abcd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterConstants.cs @@ -0,0 +1,191 @@ +// DeflaterConstants.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// This class contains constants used for deflation. + /// + internal class DeflaterConstants + { + /// + /// Set to true to enable debugging + /// + public static bool DEBUGGING + { + // Prevent warning 'CS0162: Unreachable code detected' when referencing DEBUGGING + get { return false; } + } + //public const bool DEBUGGING = false; + + /// + /// Written to Zip file to identify a stored block + /// + public const int STORED_BLOCK = 0; + + /// + /// Identifies static tree in Zip file + /// + public const int STATIC_TREES = 1; + + /// + /// Identifies dynamic tree in Zip file + /// + public const int DYN_TREES = 2; + + /// + /// Header flag indicating a preset dictionary for deflation + /// + public const int PRESET_DICT = 0x20; + + /// + /// Sets internal buffer sizes for Huffman encoding + /// + public const int DEFAULT_MEM_LEVEL = 8; + + /// + /// Internal compression engine constant + /// + public const int MAX_MATCH = 258; + + /// + /// Internal compression engine constant + /// + public const int MIN_MATCH = 3; + + /// + /// Internal compression engine constant + /// + public const int MAX_WBITS = 15; + + /// + /// Internal compression engine constant + /// + public const int WSIZE = 1 << MAX_WBITS; + + /// + /// Internal compression engine constant + /// + public const int WMASK = WSIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_BITS = DEFAULT_MEM_LEVEL + 7; + + /// + /// Internal compression engine constant + /// + public const int HASH_SIZE = 1 << HASH_BITS; + + /// + /// Internal compression engine constant + /// + public const int HASH_MASK = HASH_SIZE - 1; + + /// + /// Internal compression engine constant + /// + public const int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; + + /// + /// Internal compression engine constant + /// + public const int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + + /// + /// Internal compression engine constant + /// + public const int MAX_DIST = WSIZE - MIN_LOOKAHEAD; + + /// + /// Internal compression engine constant + /// + public const int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); + + /// + /// Internal compression engine constant + /// + public static int MAX_BLOCK_SIZE = Math.Min(65535, PENDING_BUF_SIZE - 5); + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_STORED = 0; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_FAST = 1; + + /// + /// Internal compression engine constant + /// + public const int DEFLATE_SLOW = 2; + + /// + /// Internal compression engine constant + /// + public static int[] GOOD_LENGTH = { 0, 4, 4, 4, 4, 8, 8, 8, 32, 32 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_LAZY = { 0, 4, 5, 6, 4, 16, 16, 32, 128, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] NICE_LENGTH = { 0, 8, 16, 32, 16, 32, 128, 128, 258, 258 }; + + /// + /// Internal compression engine constant + /// + public static int[] MAX_CHAIN = { 0, 4, 8, 32, 16, 32, 128, 256, 1024, 4096 }; + + /// + /// Internal compression engine constant + /// + public static int[] COMPR_FUNC = { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2 }; + + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterEngine.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterEngine.cs new file mode 100644 index 00000000..ea03bced --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterEngine.cs @@ -0,0 +1,926 @@ +// DeflaterEngine.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; +using PdfSharp.SharpZipLib.Checksums; + +// ReSharper disable RedundantThisQualifier + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// Strategies for deflater + /// + internal enum DeflateStrategy + { + /// + /// The default strategy + /// + Default = 0, + + /// + /// This strategy will only allow longer string repetitions. It is + /// useful for random data with a small character set. + /// + Filtered = 1, + + + /// + /// This strategy will not look for string repetitions at all. It + /// only encodes with Huffman trees (which means, that more common + /// characters get a smaller encoding. + /// + HuffmanOnly = 2 + } + + // DEFLATE ALGORITHM: + // + // The uncompressed stream is inserted into the window array. When + // the window array is full the first half is thrown away and the + // second half is copied to the beginning. + // + // The head array is a hash table. Three characters build a hash value + // and they the value points to the corresponding index in window of + // the last string with this hash. The prev array implements a + // linked list of matches with the same hash: prev[index & WMASK] points + // to the previous index with the same hash. + // + + + /// + /// Low level compression engine for deflate algorithm which uses a 32K sliding window + /// with secondary compression from Huffman/Shannon-Fano codes. + /// + internal class DeflaterEngine : DeflaterConstants + { + #region Constants + const int TooFar = 4096; + #endregion + + #region Constructors + /// + /// Construct instance with pending buffer + /// + /// + /// Pending buffer to use + /// > + public DeflaterEngine(DeflaterPending pending) + { + this.pending = pending; + huffman = new DeflaterHuffman(pending); + adler = new Adler32(); + + window = new byte[2 * WSIZE]; + head = new short[HASH_SIZE]; + prev = new short[WSIZE]; + + // We start at index 1, to avoid an implementation deficiency, that + // we cannot build a repeat pattern at index 0. + blockStart = strstart = 1; + } + + #endregion + + /// + /// Deflate drives actual compression of data + /// + /// True to flush input buffers + /// Finish deflation with the current input. + /// Returns true if progress has been made. + public bool Deflate(bool flush, bool finish) + { + bool progress; + do + { + FillWindow(); + bool canFlush = flush && (inputOff == inputEnd); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.WriteLine("window: [" + blockStart + "," + strstart + "," + + lookahead + "], " + compressionFunction + "," + canFlush); + } +#endif + switch (compressionFunction) + { + case DEFLATE_STORED: + progress = DeflateStored(canFlush, finish); + break; + case DEFLATE_FAST: + progress = DeflateFast(canFlush, finish); + break; + case DEFLATE_SLOW: + progress = DeflateSlow(canFlush, finish); + break; + default: + throw new InvalidOperationException("unknown compressionFunction"); + } + } while (pending.IsFlushed && progress); // repeat while we have no pending output and progress was made + return progress; + } + + /// + /// Sets input data to be deflated. Should only be called when NeedsInput() + /// returns true + /// + /// The buffer containing input data. + /// The offset of the first byte of data. + /// The number of bytes of data to use as input. + public void SetInput(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (offset < 0) + { + throw new ArgumentOutOfRangeException("offset"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count"); + } + + if (inputOff < inputEnd) + { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = offset + count; + + /* We want to throw an ArrayIndexOutOfBoundsException early. The + * check is very tricky: it also handles integer wrap around. + */ + if ((offset > end) || (end > buffer.Length)) + { + throw new ArgumentOutOfRangeException("count"); + } + + inputBuf = buffer; + inputOff = offset; + inputEnd = end; + } + + /// + /// Determines if more input is needed. + /// + /// Return true if input is needed via SetInput + public bool NeedsInput() + { + return (inputEnd == inputOff); + } + + /// + /// Set compression dictionary + /// + /// The buffer containing the dictionary data + /// The offset in the buffer for the first byte of data + /// The length of the dictionary data. + public void SetDictionary(byte[] buffer, int offset, int length) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (strstart != 1) ) + { + throw new InvalidOperationException("strstart not 1"); + } +#endif + adler.Update(buffer, offset, length); + if (length < MIN_MATCH) + { + return; + } + + if (length > MAX_DIST) + { + offset += length - MAX_DIST; + length = MAX_DIST; + } + + System.Array.Copy(buffer, offset, window, strstart, length); + + UpdateHash(); + --length; + while (--length > 0) + { + InsertString(); + strstart++; + } + strstart += 2; + blockStart = strstart; + } + + /// + /// Reset internal state + /// + public void Reset() + { + huffman.Reset(); + adler.Reset(); + blockStart = strstart = 1; + lookahead = 0; + totalIn = 0; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + + for (int i = 0; i < HASH_SIZE; i++) + { + head[i] = 0; + } + + for (int i = 0; i < WSIZE; i++) + { + prev[i] = 0; + } + } + + /// + /// Reset Adler checksum + /// + public void ResetAdler() + { + adler.Reset(); + } + + /// + /// Get current value of Adler checksum + /// + public int Adler + { + get + { + return unchecked((int)adler.Value); + } + } + + /// + /// Total data processed + /// + public long TotalIn + { + get + { + return totalIn; + } + } + + /// + /// Get/set the deflate strategy + /// + public DeflateStrategy Strategy + { + get + { + return strategy; + } + set + { + strategy = value; + } + } + + /// + /// Set the deflate level (0-9) + /// + /// The value to set the level to. + public void SetLevel(int level) + { + if ((level < 0) || (level > 9)) + { + throw new ArgumentOutOfRangeException("level"); + } + + goodLength = DeflaterConstants.GOOD_LENGTH[level]; + max_lazy = DeflaterConstants.MAX_LAZY[level]; + niceLength = DeflaterConstants.NICE_LENGTH[level]; + max_chain = DeflaterConstants.MAX_CHAIN[level]; + + if (DeflaterConstants.COMPR_FUNC[level] != compressionFunction) + { + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.WriteLine("Change from " + compressionFunction + " to " + + DeflaterConstants.COMPR_FUNC[level]); + } +#endif + switch (compressionFunction) + { + case DEFLATE_STORED: + if (strstart > blockStart) + { + huffman.FlushStoredBlock(window, blockStart, + strstart - blockStart, false); + blockStart = strstart; + } + UpdateHash(); + break; + + case DEFLATE_FAST: + if (strstart > blockStart) + { + huffman.FlushBlock(window, blockStart, strstart - blockStart, + false); + blockStart = strstart; + } + break; + + case DEFLATE_SLOW: + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + if (strstart > blockStart) + { + huffman.FlushBlock(window, blockStart, strstart - blockStart, false); + blockStart = strstart; + } + prevAvailable = false; + matchLen = MIN_MATCH - 1; + break; + } + compressionFunction = COMPR_FUNC[level]; + } + } + + /// + /// Fill the window + /// + public void FillWindow() + { + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (strstart >= WSIZE + MAX_DIST) + { + SlideWindow(); + } + + /* If there is not enough lookahead, but still some input left, + * read in the input + */ + while (lookahead < DeflaterConstants.MIN_LOOKAHEAD && inputOff < inputEnd) + { + int more = 2 * WSIZE - lookahead - strstart; + + if (more > inputEnd - inputOff) + { + more = inputEnd - inputOff; + } + + System.Array.Copy(inputBuf, inputOff, window, strstart + lookahead, more); + adler.Update(inputBuf, inputOff, more); + + inputOff += more; + totalIn += more; + lookahead += more; + } + + if (lookahead >= MIN_MATCH) + { + UpdateHash(); + } + } + + void UpdateHash() + { + /* + if (DEBUGGING) { + Console.WriteLine("updateHash: "+strstart); + } + */ + ins_h = (window[strstart] << HASH_SHIFT) ^ window[strstart + 1]; + } + + /// + /// Inserts the current string in the head hash and returns the previous + /// value for this hash. + /// + /// The previous hash value + int InsertString() + { + short match; + int hash = ((ins_h << HASH_SHIFT) ^ window[strstart + (MIN_MATCH - 1)]) & HASH_MASK; + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + if (hash != (((window[strstart] << (2*HASH_SHIFT)) ^ + (window[strstart + 1] << HASH_SHIFT) ^ + (window[strstart + 2])) & HASH_MASK)) { + throw new SharpZipBaseException("hash inconsistent: " + hash + "/" + +window[strstart] + "," + +window[strstart + 1] + "," + +window[strstart + 2] + "," + HASH_SHIFT); + } + } +#endif + prev[strstart & WMASK] = match = head[hash]; + head[hash] = unchecked((short)strstart); + ins_h = hash; + return match & 0xffff; + } + + void SlideWindow() + { + Array.Copy(window, WSIZE, window, 0, WSIZE); + matchStart -= WSIZE; + strstart -= WSIZE; + blockStart -= WSIZE; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). + for (int i = 0; i < HASH_SIZE; ++i) + { + int m = head[i] & 0xffff; + head[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); + } + + // Slide the prev table. + for (int i = 0; i < WSIZE; i++) + { + int m = prev[i] & 0xffff; + prev[i] = (short)(m >= WSIZE ? (m - WSIZE) : 0); + } + } + + /// + /// Find the best (longest) string in the window matching the + /// string starting at strstart. + /// + /// Preconditions: + /// + /// strstart + MAX_MATCH <= window.length. + /// + /// + /// True if a match greater than the minimum length is found + bool FindLongestMatch(int curMatch) + { + int chainLength = this.max_chain; + int niceLength = this.niceLength; + short[] prev = this.prev; + int scan = this.strstart; + int match; + int best_end = this.strstart + matchLen; + int best_len = Math.Max(matchLen, MIN_MATCH - 1); + + int limit = Math.Max(strstart - MAX_DIST, 0); + + int strend = strstart + MAX_MATCH - 1; + byte scan_end1 = window[best_end - 1]; + byte scan_end = window[best_end]; + + // Do not waste too much time if we already have a good match: + if (best_len >= this.goodLength) + { + chainLength >>= 2; + } + + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (niceLength > lookahead) + { + niceLength = lookahead; + } + +#if DebugDeflation + + if (DeflaterConstants.DEBUGGING && (strstart > 2 * WSIZE - MIN_LOOKAHEAD)) + { + throw new InvalidOperationException("need lookahead"); + } +#endif + + do + { + +#if DebugDeflation + + if (DeflaterConstants.DEBUGGING && (curMatch >= strstart) ) + { + throw new InvalidOperationException("no future"); + } +#endif + if (window[curMatch + best_len] != scan_end || + window[curMatch + best_len - 1] != scan_end1 || + window[curMatch] != window[scan] || + window[curMatch + 1] != window[scan + 1]) + { + continue; + } + + match = curMatch + 2; + scan += 2; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart + 258. + */ + while ( + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + (scan < strend)) + { + // Do nothing + } + + if (scan > best_end) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (ins_h == 0) ) + Console.Error.WriteLine("Found match: " + curMatch + "-" + (scan - strstart)); +#endif + matchStart = curMatch; + best_end = scan; + best_len = scan - strstart; + + if (best_len >= niceLength) + { + break; + } + + scan_end1 = window[best_end - 1]; + scan_end = window[best_end]; + } + scan = strstart; + } while ((curMatch = (prev[curMatch & WMASK] & 0xffff)) > limit && --chainLength != 0); + + matchLen = Math.Min(best_len, lookahead); + return matchLen >= MIN_MATCH; + } + + bool DeflateStored(bool flush, bool finish) + { + if (!flush && (lookahead == 0)) + { + return false; + } + + strstart += lookahead; + lookahead = 0; + + int storedLength = strstart - blockStart; + + if ((storedLength >= DeflaterConstants.MAX_BLOCK_SIZE) || // Block is full + (blockStart < WSIZE && storedLength >= MAX_DIST) || // Block may move out of window + flush) + { + bool lastBlock = finish; + if (storedLength > DeflaterConstants.MAX_BLOCK_SIZE) + { + storedLength = DeflaterConstants.MAX_BLOCK_SIZE; + lastBlock = false; + } + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + Console.WriteLine("storedBlock[" + storedLength + "," + lastBlock + "]"); + } +#endif + + huffman.FlushStoredBlock(window, blockStart, storedLength, lastBlock); + blockStart += storedLength; + return !lastBlock; + } + return true; + } + + bool DeflateFast(bool flush, bool finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + // We are flushing everything + huffman.FlushBlock(window, blockStart, strstart - blockStart, finish); + blockStart = strstart; + return false; + } + + if (strstart > 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as FindLongestMatch needs this. + * This should only happen when flushing and the window + * is almost full. + */ + SlideWindow(); + } + + int hashHead; + if (lookahead >= MIN_MATCH && + (hashHead = InsertString()) != 0 && + strategy != DeflateStrategy.HuffmanOnly && + strstart - hashHead <= MAX_DIST && + FindLongestMatch(hashHead)) + { + // longestMatch sets matchStart and matchLen +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) { + if (window[strstart + i] != window[matchStart + i]) { + throw new SharpZipBaseException("Match failure"); + } + } + } +#endif + + bool full = huffman.TallyDist(strstart - matchStart, matchLen); + + lookahead -= matchLen; + if (matchLen <= max_lazy && lookahead >= MIN_MATCH) + { + while (--matchLen > 0) + { + ++strstart; + InsertString(); + } + ++strstart; + } + else + { + strstart += matchLen; + if (lookahead >= MIN_MATCH - 1) + { + UpdateHash(); + } + } + matchLen = MIN_MATCH - 1; + if (!full) + { + continue; + } + } + else + { + // No match found + huffman.TallyLit(window[strstart] & 0xff); + ++strstart; + --lookahead; + } + + if (huffman.IsFull()) + { + bool lastBlock = finish && (lookahead == 0); + huffman.FlushBlock(window, blockStart, strstart - blockStart, lastBlock); + blockStart = strstart; + return !lastBlock; + } + } + return true; + } + + bool DeflateSlow(bool flush, bool finish) + { + if (lookahead < MIN_LOOKAHEAD && !flush) + { + return false; + } + + while (lookahead >= MIN_LOOKAHEAD || flush) + { + if (lookahead == 0) + { + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + prevAvailable = false; + + // We are flushing everything +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && !flush) + { + throw new SharpZipBaseException("Not flushing, but no lookahead"); + } +#endif + huffman.FlushBlock(window, blockStart, strstart - blockStart, + finish); + blockStart = strstart; + return false; + } + + if (strstart >= 2 * WSIZE - MIN_LOOKAHEAD) + { + /* slide window, as FindLongestMatch needs this. + * This should only happen when flushing and the window + * is almost full. + */ + SlideWindow(); + } + + int prevMatch = matchStart; + int prevLen = matchLen; + if (lookahead >= MIN_MATCH) + { + + int hashHead = InsertString(); + + if (strategy != DeflateStrategy.HuffmanOnly && + hashHead != 0 && + strstart - hashHead <= MAX_DIST && + FindLongestMatch(hashHead)) + { + + // longestMatch sets matchStart and matchLen + + // Discard match if too small and too far away + if (matchLen <= 5 && (strategy == DeflateStrategy.Filtered || (matchLen == MIN_MATCH && strstart - matchStart > TooFar))) + { + matchLen = MIN_MATCH - 1; + } + } + } + + // previous match was better + if ((prevLen >= MIN_MATCH) && (matchLen <= prevLen)) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) + { + for (int i = 0 ; i < matchLen; i++) { + if (window[strstart-1+i] != window[prevMatch + i]) + throw new SharpZipBaseException(); + } + } +#endif + huffman.TallyDist(strstart - 1 - prevMatch, prevLen); + prevLen -= 2; + do + { + strstart++; + lookahead--; + if (lookahead >= MIN_MATCH) + { + InsertString(); + } + } while (--prevLen > 0); + + strstart++; + lookahead--; + prevAvailable = false; + matchLen = MIN_MATCH - 1; + } + else + { + if (prevAvailable) + { + huffman.TallyLit(window[strstart - 1] & 0xff); + } + prevAvailable = true; + strstart++; + lookahead--; + } + + if (huffman.IsFull()) + { + int len = strstart - blockStart; + if (prevAvailable) + { + len--; + } + bool lastBlock = (finish && (lookahead == 0) && !prevAvailable); + huffman.FlushBlock(window, blockStart, len, lastBlock); + blockStart += len; + return !lastBlock; + } + } + return true; + } + + #region Instance Fields + + // Hash index of string to be inserted + int ins_h; + + /// + /// Hashtable, hashing three characters to an index for window, so + /// that window[index]..window[index+2] have this hash code. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xffff. + /// + short[] head; + + /// + /// prev[index & WMASK] points to the previous index that has the + /// same hash code as the string starting at index. This way + /// entries with the same hash code are in a linked list. + /// Note that the array should really be unsigned short, so you need + /// to and the values with 0xffff. + /// + short[] prev; + + int matchStart; + // Length of best match + int matchLen; + // Set if previous match exists + bool prevAvailable; + int blockStart; + + /// + /// Points to the current character in the window. + /// + int strstart; + + /// + /// lookahead is the number of characters starting at strstart in + /// window that are valid. + /// So window[strstart] until window[strstart+lookahead-1] are valid + /// characters. + /// + int lookahead; + + /// + /// This array contains the part of the uncompressed stream that + /// is of relevance. The current character is indexed by strstart. + /// + byte[] window; + + DeflateStrategy strategy; + int max_chain, max_lazy, niceLength, goodLength; + + /// + /// The current compression function. + /// + int compressionFunction; + + /// + /// The input data for compression. + /// + byte[] inputBuf; + + /// + /// The total bytes of input read. + /// + long totalIn; + + /// + /// The offset into inputBuf, where input data starts. + /// + int inputOff; + + /// + /// The end offset of the input data. + /// + int inputEnd; + + DeflaterPending pending; + DeflaterHuffman huffman; + + /// + /// The adler checksum + /// + Adler32 adler; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterHuffman.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterHuffman.cs new file mode 100644 index 00000000..31b4cf6b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterHuffman.cs @@ -0,0 +1,999 @@ +// DeflaterHuffman.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +// ReSharper disable RedundantThisQualifier + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// This is the DeflaterHuffman class. + /// + /// This class is not thread safe. This is inherent in the API, due + /// to the split of Deflate and SetInput. + /// + /// author of the original java version : Jochen Hoenicke + /// + internal class DeflaterHuffman + { + const int BUFSIZE = 1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6); + const int LITERAL_NUM = 286; + + // Number of distance codes + const int DIST_NUM = 30; + // Number of codes used to transfer bit lengths + const int BITLEN_NUM = 19; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + const int REP_3_6 = 16; + // repeat a zero length 3-10 times (3 bits of repeat count) + const int REP_3_10 = 17; + // repeat a zero length 11-138 times (7 bits of repeat count) + const int REP_11_138 = 18; + + const int EOF_SYMBOL = 256; + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit length codes. + static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + static readonly byte[] bit4Reverse = { + 0, + 8, + 4, + 12, + 2, + 10, + 6, + 14, + 1, + 9, + 5, + 13, + 3, + 11, + 7, + 15 + }; + + static short[] staticLCodes; + static byte[] staticLLength; + static short[] staticDCodes; + static byte[] staticDLength; + + class Tree + { + #region Instance Fields + public short[] freqs; + + public byte[] length; + + public int minNumCodes; + + public int numCodes; + + short[] codes; + int[] bl_counts; + int maxLength; + DeflaterHuffman dh; + #endregion + + #region Constructors + public Tree(DeflaterHuffman dh, int elems, int minCodes, int maxLength) + { + this.dh = dh; + this.minNumCodes = minCodes; + this.maxLength = maxLength; + freqs = new short[elems]; + bl_counts = new int[maxLength]; + } + + #endregion + + /// + /// Resets the internal state of the tree + /// + public void Reset() + { + for (int i = 0; i < freqs.Length; i++) + { + freqs[i] = 0; + } + codes = null; + length = null; + } + + public void WriteSymbol(int code) + { + // if (DeflaterConstants.DEBUGGING) { + // freqs[code]--; + // // Console.Write("writeSymbol("+freqs.length+","+code+"): "); + // } + dh.pending.WriteBits(codes[code] & 0xffff, length[code]); + } + + /// + /// Check that all frequencies are zero + /// + /// + /// At least one frequency is non-zero + /// + public void CheckEmpty() + { + bool empty = true; + for (int i = 0; i < freqs.Length; i++) + { + if (freqs[i] != 0) + { + //Console.WriteLine("freqs[" + i + "] == " + freqs[i]); + empty = false; + } + } + + if (!empty) + { + throw new SharpZipBaseException("!Empty"); + } + } + + /// + /// Set static codes and length + /// + /// new codes + /// length for new codes + public void SetStaticCodes(short[] staticCodes, byte[] staticLengths) + { + codes = staticCodes; + length = staticLengths; + } + + /// + /// Build dynamic codes and lengths + /// + public void BuildCodes() + { + int numSymbols = freqs.Length; + int[] nextCode = new int[maxLength]; + int code = 0; + + codes = new short[freqs.Length]; + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("buildCodes: "+freqs.Length); + // } + + for (int bits = 0; bits < maxLength; bits++) + { + nextCode[bits] = code; + code += bl_counts[bits] << (15 - bits); + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("bits: " + ( bits + 1) + " count: " + bl_counts[bits] + // +" nextCode: "+code); + // } + } + +#if DebugDeflation + if ( DeflaterConstants.DEBUGGING && (code != 65536) ) + { + throw new SharpZipBaseException("Inconsistent bl_counts!"); + } +#endif + for (int i = 0; i < numCodes; i++) + { + int bits = length[i]; + if (bits > 0) + { + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("codes["+i+"] = rev(" + nextCode[bits-1]+"), + // +bits); + // } + + codes[i] = BitReverse(nextCode[bits - 1]); + nextCode[bits - 1] += 1 << (16 - bits); + } + } + } + + public void BuildTree() + { + int numSymbols = freqs.Length; + + /* heap is a priority queue, sorted by frequency, least frequent + * nodes first. The heap is a binary tree, with the property, that + * the parent node is smaller than both child nodes. This assures + * that the smallest node is the first parent. + * + * The binary tree is encoded in an array: 0 is root node and + * the nodes 2*n+1, 2*n+2 are the child nodes of node n. + */ + int[] heap = new int[numSymbols]; + int heapLen = 0; + int maxCode = 0; + for (int n = 0; n < numSymbols; n++) + { + int freq = freqs[n]; + if (freq != 0) + { + // Insert n into heap + int pos = heapLen++; + int ppos; + while (pos > 0 && freqs[heap[ppos = (pos - 1) / 2]] > freq) + { + heap[pos] = heap[ppos]; + pos = ppos; + } + heap[pos] = n; + + maxCode = n; + } + } + + /* We could encode a single literal with 0 bits but then we + * don't see the literals. Therefore we force at least two + * literals to avoid this case. We don't care about order in + * this case, both literals get a 1 bit code. + */ + while (heapLen < 2) + { + int node = maxCode < 2 ? ++maxCode : 0; + heap[heapLen++] = node; + } + + numCodes = Math.Max(maxCode + 1, minNumCodes); + + int numLeafs = heapLen; + int[] childs = new int[4 * heapLen - 2]; + int[] values = new int[2 * heapLen - 1]; + int numNodes = numLeafs; + for (int i = 0; i < heapLen; i++) + { + int node = heap[i]; + childs[2 * i] = node; + childs[2 * i + 1] = -1; + values[i] = freqs[node] << 8; + heap[i] = i; + } + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do + { + int first = heap[0]; + int last = heap[--heapLen]; + + // Propagate the hole to the leafs of the heap + int ppos = 0; + int path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) + { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = path * 2 + 1; + } + + /* Now propagate the last element down along path. Normally + * it shouldn't go too deep. + */ + int lastVal = values[last]; + while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) + { + heap[path] = heap[ppos]; + } + heap[path] = last; + + + int second = heap[0]; + + // Create a new node father of first and second + last = numNodes++; + childs[2 * last] = first; + childs[2 * last + 1] = second; + int mindepth = Math.Min(values[first] & 0xff, values[second] & 0xff); + values[last] = lastVal = values[first] + values[second] - mindepth + 1; + + // Again, propagate the hole to the leafs + ppos = 0; + path = 1; + + while (path < heapLen) + { + if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) + { + path++; + } + + heap[ppos] = heap[path]; + ppos = path; + path = ppos * 2 + 1; + } + + // Now propagate the new element down along path + while ((path = ppos) > 0 && values[heap[ppos = (path - 1) / 2]] > lastVal) + { + heap[path] = heap[ppos]; + } + heap[path] = last; + } while (heapLen > 1); + + if (heap[0] != childs.Length / 2 - 1) + { + throw new SharpZipBaseException("Heap invariant violated"); + } + + BuildLength(childs); + } + + /// + /// Get encoded length + /// + /// Encoded length, the sum of frequencies * lengths + public int GetEncodedLength() + { + int len = 0; + for (int i = 0; i < freqs.Length; i++) + { + len += freqs[i] * length[i]; + } + return len; + } + + /// + /// Scan a literal or distance tree to determine the frequencies of the codes + /// in the bit length tree. + /// + public void CalcBLFreq(Tree blTree) + { + int max_count; /* max repeat count */ + int min_count; /* min repeat count */ + int count; /* repeat count of the current code */ + int curlen = -1; /* length of current code */ + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.freqs[nextlen]++; + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + { + break; + } + } + + if (count < min_count) + { + blTree.freqs[curlen] += (short)count; + } + else if (curlen != 0) + { + blTree.freqs[REP_3_6]++; + } + else if (count <= 10) + { + blTree.freqs[REP_3_10]++; + } + else + { + blTree.freqs[REP_11_138]++; + } + } + } + + /// + /// Write tree values + /// + /// Tree to write + public void WriteTree(Tree blTree) + { + int max_count; // max repeat count + int min_count; // min repeat count + int count; // repeat count of the current code + int curlen = -1; // length of current code + + int i = 0; + while (i < numCodes) + { + count = 1; + int nextlen = length[i]; + if (nextlen == 0) + { + max_count = 138; + min_count = 3; + } + else + { + max_count = 6; + min_count = 3; + if (curlen != nextlen) + { + blTree.WriteSymbol(nextlen); + count = 0; + } + } + curlen = nextlen; + i++; + + while (i < numCodes && curlen == length[i]) + { + i++; + if (++count >= max_count) + { + break; + } + } + + if (count < min_count) + { + while (count-- > 0) + { + blTree.WriteSymbol(curlen); + } + } + else if (curlen != 0) + { + blTree.WriteSymbol(REP_3_6); + dh.pending.WriteBits(count - 3, 2); + } + else if (count <= 10) + { + blTree.WriteSymbol(REP_3_10); + dh.pending.WriteBits(count - 3, 3); + } + else + { + blTree.WriteSymbol(REP_11_138); + dh.pending.WriteBits(count - 11, 7); + } + } + } + + void BuildLength(int[] childs) + { + this.length = new byte[freqs.Length]; + int numNodes = childs.Length / 2; + int numLeafs = (numNodes + 1) / 2; + int overflow = 0; + + for (int i = 0; i < maxLength; i++) + { + bl_counts[i] = 0; + } + + // First calculate optimal bit lengths + int[] lengths = new int[numNodes]; + lengths[numNodes - 1] = 0; + + for (int i = numNodes - 1; i >= 0; i--) + { + if (childs[2 * i + 1] != -1) + { + int bitLength = lengths[i] + 1; + if (bitLength > maxLength) + { + bitLength = maxLength; + overflow++; + } + lengths[childs[2 * i]] = lengths[childs[2 * i + 1]] = bitLength; + } + else + { + // A leaf node + int bitLength = lengths[i]; + bl_counts[bitLength - 1]++; + this.length[childs[2 * i]] = (byte)lengths[i]; + } + } + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Tree "+freqs.Length+" lengths:"); + // for (int i=0; i < numLeafs; i++) { + // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + // + " len: "+length[childs[2*i]]); + // } + // } + + if (overflow == 0) + { + return; + } + + int incrBitLen = maxLength - 1; + do + { + // Find the first bit length which could increase: + while (bl_counts[--incrBitLen] == 0) + ; + + // Move this node one down and remove a corresponding + // number of overflow nodes. + do + { + bl_counts[incrBitLen]--; + bl_counts[++incrBitLen]++; + overflow -= 1 << (maxLength - 1 - incrBitLen); + } while (overflow > 0 && incrBitLen < maxLength - 1); + } while (overflow > 0); + + /* We may have overshot above. Move some nodes from maxLength to + * maxLength-1 in that case. + */ + bl_counts[maxLength - 1] += overflow; + bl_counts[maxLength - 2] -= overflow; + + /* Now recompute all bit lengths, scanning in increasing + * frequency. It is simpler to reconstruct all lengths instead of + * fixing only the wrong ones. This idea is taken from 'ar' + * written by Haruhiko Okumura. + * + * The nodes were inserted with decreasing frequency into the childs + * array. + */ + int nodePtr = 2 * numLeafs; + for (int bits = maxLength; bits != 0; bits--) + { + int n = bl_counts[bits - 1]; + while (n > 0) + { + int childPtr = 2 * childs[nodePtr++]; + if (childs[childPtr + 1] == -1) + { + // We found another leaf + length[childs[childPtr]] = (byte)bits; + n--; + } + } + } + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("*** After overflow elimination. ***"); + // for (int i=0; i < numLeafs; i++) { + // //Console.WriteLine("Node "+childs[2*i]+" freq: "+freqs[childs[2*i]] + // + " len: "+length[childs[2*i]]); + // } + // } + } + + } + + #region Instance Fields + /// + /// Pending buffer to use + /// + public DeflaterPending pending; + + Tree literalTree; + Tree distTree; + Tree blTree; + + // Buffer for distances + short[] d_buf; + byte[] l_buf; + int last_lit; + int extra_bits; + #endregion + + static DeflaterHuffman() + { + // See RFC 1951 3.2.6 + // Literal codes + staticLCodes = new short[LITERAL_NUM]; + staticLLength = new byte[LITERAL_NUM]; + + int i = 0; + while (i < 144) + { + staticLCodes[i] = BitReverse((0x030 + i) << 8); + staticLLength[i++] = 8; + } + + while (i < 256) + { + staticLCodes[i] = BitReverse((0x190 - 144 + i) << 7); + staticLLength[i++] = 9; + } + + while (i < 280) + { + staticLCodes[i] = BitReverse((0x000 - 256 + i) << 9); + staticLLength[i++] = 7; + } + + while (i < LITERAL_NUM) + { + staticLCodes[i] = BitReverse((0x0c0 - 280 + i) << 8); + staticLLength[i++] = 8; + } + + // Distance codes + staticDCodes = new short[DIST_NUM]; + staticDLength = new byte[DIST_NUM]; + for (i = 0; i < DIST_NUM; i++) + { + staticDCodes[i] = BitReverse(i << 11); + staticDLength[i] = 5; + } + } + + /// + /// Construct instance with pending buffer + /// + /// Pending buffer to use + public DeflaterHuffman(DeflaterPending pending) + { + this.pending = pending; + + literalTree = new Tree(this, LITERAL_NUM, 257, 15); + distTree = new Tree(this, DIST_NUM, 1, 15); + blTree = new Tree(this, BITLEN_NUM, 4, 7); + + d_buf = new short[BUFSIZE]; + l_buf = new byte[BUFSIZE]; + } + + /// + /// Reset internal state + /// + public void Reset() + { + last_lit = 0; + extra_bits = 0; + literalTree.Reset(); + distTree.Reset(); + blTree.Reset(); + } + + /// + /// Write all trees to pending buffer + /// + /// The number/rank of treecodes to send. + public void SendAllTrees(int blTreeCodes) + { + blTree.BuildCodes(); + literalTree.BuildCodes(); + distTree.BuildCodes(); + pending.WriteBits(literalTree.numCodes - 257, 5); + pending.WriteBits(distTree.numCodes - 1, 5); + pending.WriteBits(blTreeCodes - 4, 4); + for (int rank = 0; rank < blTreeCodes; rank++) + { + pending.WriteBits(blTree.length[BL_ORDER[rank]], 3); + } + literalTree.WriteTree(blTree); + distTree.WriteTree(blTree); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + blTree.CheckEmpty(); + } +#endif + } + + /// + /// Compress current buffer writing data to pending buffer + /// + public void CompressBlock() + { + for (int i = 0; i < last_lit; i++) + { + int litlen = l_buf[i] & 0xff; + int dist = d_buf[i]; + if (dist-- != 0) + { + // if (DeflaterConstants.DEBUGGING) { + // Console.Write("["+(dist+1)+","+(litlen+3)+"]: "); + // } + + int lc = Lcode(litlen); + literalTree.WriteSymbol(lc); + + int bits = (lc - 261) / 4; + if (bits > 0 && bits <= 5) + { + pending.WriteBits(litlen & ((1 << bits) - 1), bits); + } + + int dc = Dcode(dist); + distTree.WriteSymbol(dc); + + bits = dc / 2 - 1; + if (bits > 0) + { + pending.WriteBits(dist & ((1 << bits) - 1), bits); + } + } + else + { + // if (DeflaterConstants.DEBUGGING) { + // if (litlen > 32 && litlen < 127) { + // Console.Write("("+(char)litlen+"): "); + // } else { + // Console.Write("{"+litlen+"}: "); + // } + // } + literalTree.WriteSymbol(litlen); + } + } + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + Console.Write("EOF: "); + } +#endif + literalTree.WriteSymbol(EOF_SYMBOL); + +#if DebugDeflation + if (DeflaterConstants.DEBUGGING) { + literalTree.CheckEmpty(); + distTree.CheckEmpty(); + } +#endif + } + + /// + /// Flush block to output with no compression + /// + /// Data to write + /// Index of first byte to write + /// Count of bytes to write + /// True if this is the last block + public void FlushStoredBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { +#if DebugDeflation + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Flushing stored block "+ storedLength); + // } +#endif + pending.WriteBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0), 3); + pending.AlignToByte(); + pending.WriteShort(storedLength); + pending.WriteShort(~storedLength); + pending.WriteBlock(stored, storedOffset, storedLength); + Reset(); + } + + /// + /// Flush block to output with compression + /// + /// Data to flush + /// Index of first byte to flush + /// Count of bytes to flush + /// True if this is the last block + public void FlushBlock(byte[] stored, int storedOffset, int storedLength, bool lastBlock) + { + literalTree.freqs[EOF_SYMBOL]++; + + // Build trees + literalTree.BuildTree(); + distTree.BuildTree(); + + // Calculate bitlen frequency + literalTree.CalcBLFreq(blTree); + distTree.CalcBLFreq(blTree); + + // Build bitlen tree + blTree.BuildTree(); + + int blTreeCodes = 4; + for (int i = 18; i > blTreeCodes; i--) + { + if (blTree.length[BL_ORDER[i]] > 0) + { + blTreeCodes = i + 1; + } + } + int opt_len = 14 + blTreeCodes * 3 + blTree.GetEncodedLength() + + literalTree.GetEncodedLength() + distTree.GetEncodedLength() + + extra_bits; + + int static_len = extra_bits; + for (int i = 0; i < LITERAL_NUM; i++) + { + static_len += literalTree.freqs[i] * staticLLength[i]; + } + for (int i = 0; i < DIST_NUM; i++) + { + static_len += distTree.freqs[i] * staticDLength[i]; + } + if (opt_len >= static_len) + { + // Force static trees + opt_len = static_len; + } + + if (storedOffset >= 0 && storedLength + 4 < opt_len >> 3) + { + // Store Block + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("Storing, since " + storedLength + " < " + opt_len + // + " <= " + static_len); + // } + FlushStoredBlock(stored, storedOffset, storedLength, lastBlock); + } + else if (opt_len == static_len) + { + // Encode with static tree + pending.WriteBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0), 3); + literalTree.SetStaticCodes(staticLCodes, staticLLength); + distTree.SetStaticCodes(staticDCodes, staticDLength); + CompressBlock(); + Reset(); + } + else + { + // Encode with dynamic tree + pending.WriteBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0), 3); + SendAllTrees(blTreeCodes); + CompressBlock(); + Reset(); + } + } + + /// + /// Get value indicating if internal buffer is full + /// + /// true if buffer is full + public bool IsFull() + { + return last_lit >= BUFSIZE; + } + + /// + /// Add literal to buffer + /// + /// Literal value to add to buffer. + /// Value indicating internal buffer is full + public bool TallyLit(int literal) + { + // if (DeflaterConstants.DEBUGGING) { + // if (lit > 32 && lit < 127) { + // //Console.WriteLine("("+(char)lit+")"); + // } else { + // //Console.WriteLine("{"+lit+"}"); + // } + // } + d_buf[last_lit] = 0; + l_buf[last_lit++] = (byte)literal; + literalTree.freqs[literal]++; + return IsFull(); + } + + /// + /// Add distance code and length to literal and distance trees + /// + /// Distance code + /// Length + /// Value indicating if internal buffer is full + public bool TallyDist(int distance, int length) + { + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("[" + distance + "," + length + "]"); + // } + + d_buf[last_lit] = (short)distance; + l_buf[last_lit++] = (byte)(length - 3); + + int lc = Lcode(length - 3); + literalTree.freqs[lc]++; + if (lc >= 265 && lc < 285) + { + extra_bits += (lc - 261) / 4; + } + + int dc = Dcode(distance - 1); + distTree.freqs[dc]++; + if (dc >= 4) + { + extra_bits += dc / 2 - 1; + } + return IsFull(); + } + + + /// + /// Reverse the bits of a 16 bit value. + /// + /// Value to reverse bits + /// Value with bits reversed + public static short BitReverse(int toReverse) + { + return (short)(bit4Reverse[toReverse & 0xF] << 12 | + bit4Reverse[(toReverse >> 4) & 0xF] << 8 | + bit4Reverse[(toReverse >> 8) & 0xF] << 4 | + bit4Reverse[toReverse >> 12]); + } + + static int Lcode(int length) + { + if (length == 255) + { + return 285; + } + + int code = 257; + while (length >= 8) + { + code += 4; + length >>= 1; + } + return code + length; + } + + static int Dcode(int distance) + { + int code = 0; + while (distance >= 4) + { + code += 2; + distance >>= 1; + } + return code + distance; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterPending.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterPending.cs new file mode 100644 index 00000000..d3ac50e6 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/DeflaterPending.cs @@ -0,0 +1,58 @@ +// DeflaterPending.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// This class stores the pending output of the Deflater. + /// + /// Author of the original java version: Jochen Hoenicke + /// + internal class DeflaterPending : PendingBuffer + { + /// + /// Construct instance with default buffer size + /// + public DeflaterPending() + : base(DeflaterConstants.PENDING_BUF_SIZE) + { + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Inflater.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Inflater.cs new file mode 100644 index 00000000..3d7020d0 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Inflater.cs @@ -0,0 +1,915 @@ +// Inflater.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; +using PdfSharp.SharpZipLib.Checksums; +using PdfSharp.SharpZipLib.Zip.Compression.Streams; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + /// + /// Inflater is used to decompress data that has been compressed according + /// to the "deflate" standard described in rfc1951. + /// + /// By default Zlib (rfc1950) headers and footers are expected in the input. + /// You can use constructor public Inflater(bool noHeader) passing true + /// if there is no Zlib header information + /// + /// The usage is as following. First you have to set some input with + /// SetInput(), then Inflate() it. If inflate doesn't + /// inflate any bytes there may be three reasons: + ///
    + ///
  • IsNeedingInput() returns true because the input buffer is empty. + /// You have to provide more input with SetInput(). + /// NOTE: IsNeedingInput() also returns true when, the stream is finished. + ///
  • + ///
  • IsNeedingDictionary() returns true, you have to provide a preset + /// dictionary with SetDictionary().
  • + ///
  • IsFinished returns true, the inflater has finished.
  • + ///
+ /// Once the first output byte is produced, a dictionary will not be + /// needed at a later stage. + /// + /// Author of the original java version: John Leuner, Jochen Hoenicke + ///
+ internal class Inflater + { + #region Constants/Readonly + /// + /// Copy lengths for literal codes 257..285 + /// + static readonly int[] CPLENS = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 + }; + + /// + /// Extra bits for literal codes 257..285 + /// + static readonly int[] CPLEXT = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + /// + /// Copy offsets for distance codes 0..29 + /// + static readonly int[] CPDIST = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577 + }; + + /// + /// Extra bits for distance codes + /// + static readonly int[] CPDEXT = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13 + }; + + /// + /// These are the possible states for an inflater + /// + const int DECODE_HEADER = 0; + const int DECODE_DICT = 1; + const int DECODE_BLOCKS = 2; + const int DECODE_STORED_LEN1 = 3; + const int DECODE_STORED_LEN2 = 4; + const int DECODE_STORED = 5; + const int DECODE_DYN_HEADER = 6; + const int DECODE_HUFFMAN = 7; + const int DECODE_HUFFMAN_LENBITS = 8; + const int DECODE_HUFFMAN_DIST = 9; + const int DECODE_HUFFMAN_DISTBITS = 10; + const int DECODE_CHKSUM = 11; + const int FINISHED = 12; + #endregion + + #region Instance Fields + /// + /// This variable contains the current state. + /// + int mode; + + /// + /// The adler checksum of the dictionary or of the decompressed + /// stream, as it is written in the header resp. footer of the + /// compressed stream. + /// Only valid if mode is DECODE_DICT or DECODE_CHKSUM. + /// + int readAdler; + + /// + /// The number of bits needed to complete the current state. This + /// is valid, if mode is DECODE_DICT, DECODE_CHKSUM, + /// DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. + /// + int neededBits; + int repLength; + int repDist; + int uncomprLen; + + /// + /// True, if the last block flag was set in the last block of the + /// inflated stream. This means that the stream ends after the + /// current block. + /// + bool isLastBlock; + + /// + /// The total number of inflated bytes. + /// + long totalOut; + + /// + /// The total number of bytes set with setInput(). This is not the + /// value returned by the TotalIn property, since this also includes the + /// unprocessed input. + /// + long totalIn; + + /// + /// This variable stores the noHeader flag that was given to the constructor. + /// True means, that the inflated stream doesn't contain a Zlib header or + /// footer. + /// + bool noHeader; + + StreamManipulator input; + OutputWindow outputWindow; + InflaterDynHeader dynHeader; + InflaterHuffmanTree litlenTree, distTree; + Adler32 adler; + #endregion + + #region Constructors + /// + /// Creates a new inflater or RFC1951 decompressor + /// RFC1950/Zlib headers and footers will be expected in the input data + /// + public Inflater() + : this(false) + { + } + + /// + /// Creates a new inflater. + /// + /// + /// True if no RFC1950/Zlib header and footer fields are expected in the input data + /// + /// This is used for GZIPed/Zipped input. + /// + /// For compatibility with + /// Sun JDK you should provide one byte of input more than needed in + /// this case. + /// + public Inflater(bool noHeader) + { + this.noHeader = noHeader; + this.adler = new Adler32(); + input = new StreamManipulator(); + outputWindow = new OutputWindow(); + mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; + } + #endregion + + /// + /// Resets the inflater so that a new stream can be decompressed. All + /// pending input and output will be discarded. + /// + public void Reset() + { + mode = noHeader ? DECODE_BLOCKS : DECODE_HEADER; + totalIn = 0; + totalOut = 0; + input.Reset(); + outputWindow.Reset(); + dynHeader = null; + litlenTree = null; + distTree = null; + isLastBlock = false; + adler.Reset(); + } + + /// + /// Decodes a zlib/RFC1950 header. + /// + /// + /// False if more input is needed. + /// + /// + /// The header is invalid. + /// + private bool DecodeHeader() + { + int header = input.PeekBits(16); + if (header < 0) + { + return false; + } + input.DropBits(16); + + // The header is written in "wrong" byte order + header = ((header << 8) | (header >> 8)) & 0xffff; + if (header % 31 != 0) + { + throw new SharpZipBaseException("Header checksum illegal"); + } + + if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) + { + throw new SharpZipBaseException("Compression Method unknown"); + } + + /* Maximum size of the backwards window in bits. + * We currently ignore this, but we could use it to make the + * inflater window more space efficient. On the other hand the + * full window (15 bits) is needed most times, anyway. + int max_wbits = ((header & 0x7000) >> 12) + 8; + */ + + if ((header & 0x0020) == 0) + { // Dictionary flag? + mode = DECODE_BLOCKS; + } + else + { + mode = DECODE_DICT; + neededBits = 32; + } + return true; + } + + /// + /// Decodes the dictionary checksum after the deflate header. + /// + /// + /// False if more input is needed. + /// + private bool DecodeDict() + { + while (neededBits > 0) + { + int dictByte = input.PeekBits(8); + if (dictByte < 0) + { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | dictByte; + neededBits -= 8; + } + return false; + } + + /// + /// Decodes the huffman encoded symbols in the input stream. + /// + /// + /// false if more input is needed, true if output window is + /// full or the current block ends. + /// + /// + /// if deflated stream is invalid. + /// + private bool DecodeHuffman() + { + int free = outputWindow.GetFreeSpace(); + while (free >= 258) + { + int symbol; + switch (mode) + { + case DECODE_HUFFMAN: + // This is the inner loop so it is optimized a bit + while (((symbol = litlenTree.GetSymbol(input)) & ~0xff) == 0) + { + outputWindow.Write(symbol); + if (--free < 258) + { + return true; + } + } + + if (symbol < 257) + { + if (symbol < 0) + { + return false; + } + else + { + // symbol == 256: end of block + distTree = null; + litlenTree = null; + mode = DECODE_BLOCKS; + return true; + } + } + + try + { + repLength = CPLENS[symbol - 257]; + neededBits = CPLEXT[symbol - 257]; + } + catch (Exception) + { + throw new SharpZipBaseException("Illegal rep length code"); + } + goto case DECODE_HUFFMAN_LENBITS; // fall through + + case DECODE_HUFFMAN_LENBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_LENBITS; + int i = input.PeekBits(neededBits); + if (i < 0) + { + return false; + } + input.DropBits(neededBits); + repLength += i; + } + mode = DECODE_HUFFMAN_DIST; + goto case DECODE_HUFFMAN_DIST; // fall through + + case DECODE_HUFFMAN_DIST: + symbol = distTree.GetSymbol(input); + if (symbol < 0) + { + return false; + } + + try + { + repDist = CPDIST[symbol]; + neededBits = CPDEXT[symbol]; + } + catch (Exception) + { + throw new SharpZipBaseException("Illegal rep dist code"); + } + + goto case DECODE_HUFFMAN_DISTBITS; // fall through + + case DECODE_HUFFMAN_DISTBITS: + if (neededBits > 0) + { + mode = DECODE_HUFFMAN_DISTBITS; + int i = input.PeekBits(neededBits); + if (i < 0) + { + return false; + } + input.DropBits(neededBits); + repDist += i; + } + + outputWindow.Repeat(repLength, repDist); + free -= repLength; + mode = DECODE_HUFFMAN; + break; + + default: + throw new SharpZipBaseException("Inflater unknown mode"); + } + } + return true; + } + + /// + /// Decodes the adler checksum after the deflate stream. + /// + /// + /// false if more input is needed. + /// + /// + /// If checksum doesn't match. + /// + private bool DecodeChksum() + { + while (neededBits > 0) + { + int chkByte = input.PeekBits(8); + if (chkByte < 0) + { + return false; + } + input.DropBits(8); + readAdler = (readAdler << 8) | chkByte; + neededBits -= 8; + } + + if ((int)adler.Value != readAdler) + { + throw new SharpZipBaseException("Adler chksum doesn't match: " + (int)adler.Value + " vs. " + readAdler); + } + + mode = FINISHED; + return false; + } + + /// + /// Decodes the deflated stream. + /// + /// + /// false if more input is needed, or if finished. + /// + /// + /// if deflated stream is invalid. + /// + private bool Decode() + { + switch (mode) + { + case DECODE_HEADER: + return DecodeHeader(); + + case DECODE_DICT: + return DecodeDict(); + + case DECODE_CHKSUM: + return DecodeChksum(); + + case DECODE_BLOCKS: + if (isLastBlock) + { + if (noHeader) + { + mode = FINISHED; + return false; + } + else + { + input.SkipToByteBoundary(); + neededBits = 32; + mode = DECODE_CHKSUM; + return true; + } + } + + int type = input.PeekBits(3); + if (type < 0) + { + return false; + } + input.DropBits(3); + + if ((type & 1) != 0) + { + isLastBlock = true; + } + switch (type >> 1) + { + case DeflaterConstants.STORED_BLOCK: + input.SkipToByteBoundary(); + mode = DECODE_STORED_LEN1; + break; + case DeflaterConstants.STATIC_TREES: + litlenTree = InflaterHuffmanTree.defLitLenTree; + distTree = InflaterHuffmanTree.defDistTree; + mode = DECODE_HUFFMAN; + break; + case DeflaterConstants.DYN_TREES: + dynHeader = new InflaterDynHeader(); + mode = DECODE_DYN_HEADER; + break; + default: + throw new SharpZipBaseException("Unknown block type " + type); + } + return true; + + case DECODE_STORED_LEN1: + { + if ((uncomprLen = input.PeekBits(16)) < 0) + { + return false; + } + input.DropBits(16); + mode = DECODE_STORED_LEN2; + } + goto case DECODE_STORED_LEN2; // fall through + + case DECODE_STORED_LEN2: + { + int nlen = input.PeekBits(16); + if (nlen < 0) + { + return false; + } + input.DropBits(16); + if (nlen != (uncomprLen ^ 0xffff)) + { + throw new SharpZipBaseException("broken uncompressed block"); + } + mode = DECODE_STORED; + } + goto case DECODE_STORED; // fall through + + case DECODE_STORED: + { + int more = outputWindow.CopyStored(input, uncomprLen); + uncomprLen -= more; + if (uncomprLen == 0) + { + mode = DECODE_BLOCKS; + return true; + } + return !input.IsNeedingInput; + } + + case DECODE_DYN_HEADER: + if (!dynHeader.Decode(input)) + { + return false; + } + + litlenTree = dynHeader.BuildLitLenTree(); + distTree = dynHeader.BuildDistTree(); + mode = DECODE_HUFFMAN; + goto case DECODE_HUFFMAN; // fall through + + case DECODE_HUFFMAN: + case DECODE_HUFFMAN_LENBITS: + case DECODE_HUFFMAN_DIST: + case DECODE_HUFFMAN_DISTBITS: + return DecodeHuffman(); + + case FINISHED: + return false; + + default: + throw new SharpZipBaseException("Inflater.Decode unknown mode"); + } + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// The dictionary. + /// + public void SetDictionary(byte[] buffer) + { + SetDictionary(buffer, 0, buffer.Length); + } + + /// + /// Sets the preset dictionary. This should only be called, if + /// needsDictionary() returns true and it should set the same + /// dictionary, that was used for deflating. The getAdler() + /// function returns the checksum of the dictionary needed. + /// + /// + /// The dictionary. + /// + /// + /// The index into buffer where the dictionary starts. + /// + /// + /// The number of bytes in the dictionary. + /// + /// + /// No dictionary is needed. + /// + /// + /// The adler checksum for the buffer is invalid + /// + public void SetDictionary(byte[] buffer, int index, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + + if (count < 0) + { + throw new ArgumentOutOfRangeException("count"); + } + + if (!IsNeedingDictionary) + { + throw new InvalidOperationException("Dictionary is not needed"); + } + + adler.Update(buffer, index, count); + + if ((int)adler.Value != readAdler) + { + throw new SharpZipBaseException("Wrong adler checksum"); + } + adler.Reset(); + outputWindow.CopyDict(buffer, index, count); + mode = DECODE_BLOCKS; + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// the input. + /// + public void SetInput(byte[] buffer) + { + SetInput(buffer, 0, buffer.Length); + } + + /// + /// Sets the input. This should only be called, if needsInput() + /// returns true. + /// + /// + /// The source of input data + /// + /// + /// The index into buffer where the input starts. + /// + /// + /// The number of bytes of input to use. + /// + /// + /// No input is needed. + /// + /// + /// The index and/or count are wrong. + /// + public void SetInput(byte[] buffer, int index, int count) + { + input.SetInput(buffer, index, count); + totalIn += (long)count; + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether IsNeedingDictionary(), + /// IsNeedingInput() or IsFinished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// The number of bytes written to the buffer, 0 if no further + /// output can be produced. + /// + /// + /// if buffer has length 0. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buffer) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + return Inflate(buffer, 0, buffer.Length); + } + + /// + /// Inflates the compressed stream to the output buffer. If this + /// returns 0, you should check, whether needsDictionary(), + /// needsInput() or finished() returns true, to determine why no + /// further output is produced. + /// + /// + /// the output buffer. + /// + /// + /// the offset in buffer where storing starts. + /// + /// + /// the maximum number of bytes to output. + /// + /// + /// the number of bytes written to the buffer, 0 if no further output can be produced. + /// + /// + /// if count is less than 0. + /// + /// + /// if the index and / or count are wrong. + /// + /// + /// if deflated stream is invalid. + /// + public int Inflate(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (count < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("count"); +#else + throw new ArgumentOutOfRangeException("count", "count cannot be negative"); +#endif + } + + if (offset < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("offset"); +#else + throw new ArgumentOutOfRangeException("offset", "offset cannot be negative"); +#endif + } + + if (offset + count > buffer.Length) + { + throw new ArgumentException("count exceeds buffer bounds"); + } + + // Special case: count may be zero + if (count == 0) + { + if (!IsFinished) + { // -jr- 08-Nov-2003 INFLATE_BUG fix.. + Decode(); + } + return 0; + } + + int bytesCopied = 0; + + do + { + if (mode != DECODE_CHKSUM) + { + /* Don't give away any output, if we are waiting for the + * checksum in the input stream. + * + * With this trick we have always: + * IsNeedingInput() and not IsFinished() + * implies more output can be produced. + */ + int more = outputWindow.CopyOutput(buffer, offset, count); + if (more > 0) + { + adler.Update(buffer, offset, more); + offset += more; + bytesCopied += more; + totalOut += (long)more; + count -= more; + if (count == 0) + { + return bytesCopied; + } + } + } + } while (Decode() || ((outputWindow.GetAvailable() > 0) && (mode != DECODE_CHKSUM))); + return bytesCopied; + } + + /// + /// Returns true, if the input buffer is empty. + /// You should then call setInput(). + /// NOTE: This method also returns true when the stream is finished. + /// + public bool IsNeedingInput + { + get + { + return input.IsNeedingInput; + } + } + + /// + /// Returns true, if a preset dictionary is needed to inflate the input. + /// + public bool IsNeedingDictionary + { + get + { + return mode == DECODE_DICT && neededBits == 0; + } + } + + /// + /// Returns true, if the inflater has finished. This means, that no + /// input is needed and no output can be produced. + /// + public bool IsFinished + { + get + { + return mode == FINISHED && outputWindow.GetAvailable() == 0; + } + } + + /// + /// Gets the adler checksum. This is either the checksum of all + /// uncompressed bytes returned by inflate(), or if needsDictionary() + /// returns true (and thus no output was yet produced) this is the + /// adler checksum of the expected dictionary. + /// + /// + /// the adler checksum. + /// + public int Adler + { + get + { + return IsNeedingDictionary ? readAdler : (int)adler.Value; + } + } + + /// + /// Gets the total number of output bytes returned by Inflate(). + /// + /// + /// the total number of output bytes. + /// + public long TotalOut + { + get + { + return totalOut; + } + } + + /// + /// Gets the total number of processed compressed input bytes. + /// + /// + /// The total number of bytes of processed input bytes. + /// + public long TotalIn + { + get + { + return totalIn - (long)RemainingInput; + } + } + + /// + /// Gets the number of unprocessed input bytes. Useful, if the end of the + /// stream is reached and you want to further process the bytes after + /// the deflate stream. + /// + /// + /// The number of bytes of the input which have not been processed. + /// + public int RemainingInput + { + // TODO: This should be a long? + get + { + return input.AvailableBytes; + } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterDynHeader.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterDynHeader.cs new file mode 100644 index 00000000..d1056a34 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterDynHeader.cs @@ -0,0 +1,224 @@ +// InflaterDynHeader.cs +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +using PdfSharp.SharpZipLib.Zip.Compression.Streams; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + class InflaterDynHeader + { + const int LNUM = 0; + const int DNUM = 1; + const int BLNUM = 2; + const int BLLENS = 3; + const int LENS = 4; + const int REPS = 5; + + static readonly int[] repMin = { 3, 3, 11 }; + static readonly int[] repBits = { 2, 3, 7 }; + + byte[] blLens; + byte[] litdistLens; + + InflaterHuffmanTree blTree; + + int mode; + int lnum, dnum, blnum, num; + int repSymbol; + byte lastLen; + int ptr; + + static readonly int[] BL_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + public InflaterDynHeader() + { + } + + public bool Decode(StreamManipulator input) + { + decode_loop: + for (; ; ) + { + switch (mode) + { + case LNUM: + lnum = input.PeekBits(5); + if (lnum < 0) + { + return false; + } + lnum += 257; + input.DropBits(5); + // System.err.println("LNUM: "+lnum); + mode = DNUM; + goto case DNUM; // fall through + case DNUM: + dnum = input.PeekBits(5); + if (dnum < 0) + { + return false; + } + dnum++; + input.DropBits(5); + // System.err.println("DNUM: "+dnum); + num = lnum + dnum; + litdistLens = new byte[num]; + mode = BLNUM; + goto case BLNUM; // fall through + case BLNUM: + blnum = input.PeekBits(4); + if (blnum < 0) + { + return false; + } + blnum += 4; + input.DropBits(4); + blLens = new byte[19]; + ptr = 0; + // System.err.println("BLNUM: "+blnum); + mode = BLLENS; + goto case BLLENS; // fall through + case BLLENS: + while (ptr < blnum) + { + int len = input.PeekBits(3); + if (len < 0) + { + return false; + } + input.DropBits(3); + // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); + blLens[BL_ORDER[ptr]] = (byte)len; + ptr++; + } + blTree = new InflaterHuffmanTree(blLens); + blLens = null; + ptr = 0; + mode = LENS; + goto case LENS; // fall through + case LENS: + { + int symbol; + while (((symbol = blTree.GetSymbol(input)) & ~15) == 0) + { + /* Normal case: symbol in [0..15] */ + + // System.err.println("litdistLens["+ptr+"]: "+symbol); + litdistLens[ptr++] = lastLen = (byte)symbol; + + if (ptr == num) + { + /* Finished */ + return true; + } + } + + /* need more input ? */ + if (symbol < 0) + { + return false; + } + + /* otherwise repeat code */ + if (symbol >= 17) + { + /* repeat zero */ + // System.err.println("repeating zero"); + lastLen = 0; + } + else + { + if (ptr == 0) + { + throw new SharpZipBaseException(); + } + } + repSymbol = symbol - 16; + } + mode = REPS; + goto case REPS; // fall through + case REPS: + { + int bits = repBits[repSymbol]; + int count = input.PeekBits(bits); + if (count < 0) + { + return false; + } + input.DropBits(bits); + count += repMin[repSymbol]; + // System.err.println("litdistLens repeated: "+count); + + if (ptr + count > num) + { + throw new SharpZipBaseException(); + } + while (count-- > 0) + { + litdistLens[ptr++] = lastLen; + } + + if (ptr == num) + { + /* Finished */ + return true; + } + } + mode = LENS; + goto decode_loop; + } + } + } + + public InflaterHuffmanTree BuildLitLenTree() + { + byte[] litlenLens = new byte[lnum]; + Array.Copy(litdistLens, 0, litlenLens, 0, lnum); + return new InflaterHuffmanTree(litlenLens); + } + + public InflaterHuffmanTree BuildDistTree() + { + byte[] distLens = new byte[dnum]; + Array.Copy(litdistLens, lnum, distLens, 0, dnum); + return new InflaterHuffmanTree(distLens); + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs new file mode 100644 index 00000000..e6c9da52 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/InflaterHuffmanTree.cs @@ -0,0 +1,258 @@ +// InflaterHuffmanTree.cs +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +using PdfSharp.SharpZipLib.Zip.Compression.Streams; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + /// + /// Huffman tree used for inflation + /// + internal class InflaterHuffmanTree + { + static int MAX_BITLEN = 15; + short[] tree; + + /// + /// Literal length tree + /// + public static InflaterHuffmanTree defLitLenTree; + + /// + /// Distance tree + /// + public static InflaterHuffmanTree defDistTree; + + static InflaterHuffmanTree() + { + try + { + byte[] codeLengths = new byte[288]; + int i = 0; + while (i < 144) + { + codeLengths[i++] = 8; + } + while (i < 256) + { + codeLengths[i++] = 9; + } + while (i < 280) + { + codeLengths[i++] = 7; + } + while (i < 288) + { + codeLengths[i++] = 8; + } + defLitLenTree = new InflaterHuffmanTree(codeLengths); + + codeLengths = new byte[32]; + i = 0; + while (i < 32) + { + codeLengths[i++] = 5; + } + defDistTree = new InflaterHuffmanTree(codeLengths); + } + catch (Exception) + { + throw new SharpZipBaseException("InflaterHuffmanTree: static tree length illegal"); + } + } + + /// + /// Constructs a Huffman tree from the array of code lengths. + /// + /// + /// the array of code lengths + /// + public InflaterHuffmanTree(byte[] codeLengths) + { + BuildTree(codeLengths); + } + + void BuildTree(byte[] codeLengths) + { + int[] blCount = new int[MAX_BITLEN + 1]; + int[] nextCode = new int[MAX_BITLEN + 1]; + + for (int i = 0; i < codeLengths.Length; i++) + { + int bits = codeLengths[i]; + if (bits > 0) + { + blCount[bits]++; + } + } + + int code = 0; + int treeSize = 512; + for (int bits = 1; bits <= MAX_BITLEN; bits++) + { + nextCode[bits] = code; + code += blCount[bits] << (16 - bits); + if (bits >= 10) + { + /* We need an extra table for bit lengths >= 10. */ + int start = nextCode[bits] & 0x1ff80; + int end = code & 0x1ff80; + treeSize += (end - start) >> (16 - bits); + } + } + + /* -jr comment this out! doesn't work for dynamic trees and pkzip 2.04g + if (code != 65536) + { + throw new SharpZipBaseException("Code lengths don't add up properly."); + } + */ + /* Now create and fill the extra tables from longest to shortest + * bit len. This way the sub trees will be aligned. + */ + tree = new short[treeSize]; + int treePtr = 512; + for (int bits = MAX_BITLEN; bits >= 10; bits--) + { + int end = code & 0x1ff80; + code -= blCount[bits] << (16 - bits); + int start = code & 0x1ff80; + for (int i = start; i < end; i += 1 << 7) + { + tree[DeflaterHuffman.BitReverse(i)] = (short)((-treePtr << 4) | bits); + treePtr += 1 << (bits - 9); + } + } + + for (int i = 0; i < codeLengths.Length; i++) + { + int bits = codeLengths[i]; + if (bits == 0) + { + continue; + } + code = nextCode[bits]; + int revcode = DeflaterHuffman.BitReverse(code); + if (bits <= 9) + { + do + { + tree[revcode] = (short)((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < 512); + } + else + { + int subTree = tree[revcode & 511]; + int treeLen = 1 << (subTree & 15); + subTree = -(subTree >> 4); + do + { + tree[subTree | (revcode >> 9)] = (short)((i << 4) | bits); + revcode += 1 << bits; + } while (revcode < treeLen); + } + nextCode[bits] = code + (1 << (16 - bits)); + } + + } + + /// + /// Reads the next symbol from input. The symbol is encoded using the + /// huffman tree. + /// + /// + /// input the input source. + /// + /// + /// the next symbol, or -1 if not enough input is available. + /// + public int GetSymbol(StreamManipulator input) + { + int lookahead, symbol; + if ((lookahead = input.PeekBits(9)) >= 0) + { + if ((symbol = tree[lookahead]) >= 0) + { + input.DropBits(symbol & 15); + return symbol >> 4; + } + int subtree = -(symbol >> 4); + int bitlen = symbol & 15; + if ((lookahead = input.PeekBits(bitlen)) >= 0) + { + symbol = tree[subtree | (lookahead >> 9)]; + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[subtree | (lookahead >> 9)]; + if ((symbol & 15) <= bits) + { + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + return -1; + } + } + } + else + { + int bits = input.AvailableBits; + lookahead = input.PeekBits(bits); + symbol = tree[lookahead]; + if (symbol >= 0 && (symbol & 15) <= bits) + { + input.DropBits(symbol & 15); + return symbol >> 4; + } + else + { + return -1; + } + } + } + } +} + diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/PendingBuffer.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/PendingBuffer.cs new file mode 100644 index 00000000..72328c05 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/PendingBuffer.cs @@ -0,0 +1,306 @@ +// PendingBuffer.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Zip.Compression +{ + + /// + /// This class is general purpose class for writing data to a buffer. + /// + /// It allows you to write bits as well as bytes + /// Based on DeflaterPending.java + /// + /// Author of the original java version: Jochen Hoenicke + /// + internal class PendingBuffer + { + #region Instance Fields + /// + /// Internal work buffer + /// + byte[] buffer_; + + int start; + int end; + + uint bits; + int bitCount; + #endregion + + #region Constructors + /// + /// construct instance using default buffer size of 4096 + /// + public PendingBuffer() + : this(4096) + { + } + + /// + /// construct instance using specified buffer size + /// + /// + /// size to use for internal buffer + /// + public PendingBuffer(int bufferSize) + { + buffer_ = new byte[bufferSize]; + } + + #endregion + + /// + /// Clear internal state/buffers + /// + public void Reset() + { + start = end = bitCount = 0; + } + + /// + /// Write a byte to buffer + /// + /// + /// The value to write + /// + public void WriteByte(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer_[end++] = unchecked((byte)value); + } + + /// + /// Write a short value to buffer LSB first + /// + /// + /// The value to write. + /// + public void WriteShort(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer_[end++] = unchecked((byte)value); + buffer_[end++] = unchecked((byte)(value >> 8)); + } + + /// + /// write an integer LSB first + /// + /// The value to write. + public void WriteInt(int value) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer_[end++] = unchecked((byte)value); + buffer_[end++] = unchecked((byte)(value >> 8)); + buffer_[end++] = unchecked((byte)(value >> 16)); + buffer_[end++] = unchecked((byte)(value >> 24)); + } + + /// + /// Write a block of data to buffer + /// + /// data to write + /// offset of first byte to write + /// number of bytes to write + public void WriteBlock(byte[] block, int offset, int length) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + System.Array.Copy(block, offset, buffer_, end, length); + end += length; + } + + /// + /// The number of bits written to the buffer + /// + public int BitCount + { + get + { + return bitCount; + } + } + + /// + /// Align internal buffer on a byte boundary + /// + public void AlignToByte() + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + if (bitCount > 0) + { + buffer_[end++] = unchecked((byte)bits); + if (bitCount > 8) + { + buffer_[end++] = unchecked((byte)(bits >> 8)); + } + } + bits = 0; + bitCount = 0; + } + + /// + /// Write bits to internal buffer + /// + /// source of bits + /// number of bits to write + public void WriteBits(int b, int count) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } + + // if (DeflaterConstants.DEBUGGING) { + // //Console.WriteLine("writeBits("+b+","+count+")"); + // } +#endif + bits |= (uint)(b << bitCount); + bitCount += count; + if (bitCount >= 16) + { + buffer_[end++] = unchecked((byte)bits); + buffer_[end++] = unchecked((byte)(bits >> 8)); + bits >>= 16; + bitCount -= 16; + } + } + + /// + /// Write a short value to internal buffer most significant byte first + /// + /// value to write + public void WriteShortMSB(int s) + { +#if DebugDeflation + if (DeflaterConstants.DEBUGGING && (start != 0) ) + { + throw new SharpZipBaseException("Debug check: start != 0"); + } +#endif + buffer_[end++] = unchecked((byte)(s >> 8)); + buffer_[end++] = unchecked((byte)s); + } + + /// + /// Indicates if buffer has been flushed + /// + public bool IsFlushed + { + get + { + return end == 0; + } + } + + /// + /// Flushes the pending buffer into the given output array. If the + /// output array is to small, only a partial flush is done. + /// + /// The output array. + /// The offset into output array. + /// The maximum number of bytes to store. + /// The number of bytes flushed. + public int Flush(byte[] output, int offset, int length) + { + if (bitCount >= 8) + { + buffer_[end++] = unchecked((byte)bits); + bits >>= 8; + bitCount -= 8; + } + + if (length > end - start) + { + length = end - start; + System.Array.Copy(buffer_, start, output, offset, length); + start = 0; + end = 0; + } + else + { + System.Array.Copy(buffer_, start, output, offset, length); + start += length; + } + return length; + } + + /// + /// Convert internal buffer to byte array. + /// Buffer is empty on completion + /// + /// + /// The internal buffer contents converted to a byte array. + /// + public byte[] ToByteArray() + { + byte[] result = new byte[end - start]; + System.Array.Copy(buffer_, start, result, 0, result.Length); + start = 0; + end = 0; + return result; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs new file mode 100644 index 00000000..d197306a --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs @@ -0,0 +1,676 @@ +// DeflaterOutputStream.cs +// +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +// HISTORY +// 22-12-2009 DavidPierson Added AES support + +using System; +using System.IO; +using PdfSharp.SharpZipLib.Checksums; + +// ReSharper disable RedundantThisQualifier + +//#if !NETCF_1_0 +//using System.Security.Cryptography; +//using PdfSharp.SharpZipLib.Encryption; +//#endif + +namespace PdfSharp.SharpZipLib.Zip.Compression.Streams +{ + /// + /// A special stream deflating or compressing the bytes that are + /// written to it. It uses a Deflater to perform actual deflating.
+ /// Authors of the original java version: Tom Tromey, Jochen Hoenicke + ///
+ internal class DeflaterOutputStream : Stream + { + #region Constructors + /// + /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size. + /// + /// + /// the output stream where deflated output should be written. + /// + public DeflaterOutputStream(Stream baseOutputStream) + : this(baseOutputStream, new Deflater(), 512) + { + } + + /// + /// Creates a new DeflaterOutputStream with the given Deflater and + /// default buffer size. + /// + /// + /// the output stream where deflated output should be written. + /// + /// + /// the underlying deflater. + /// + public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater) + : this(baseOutputStream, deflater, 512) + { + } + + /// + /// Creates a new DeflaterOutputStream with the given Deflater and + /// buffer size. + /// + /// + /// The output stream where deflated output is written. + /// + /// + /// The underlying deflater to use + /// + /// + /// The buffer size in bytes to use when deflating (minimum value 512) + /// + /// + /// bufsize is less than or equal to zero. + /// + /// + /// baseOutputStream does not support writing + /// + /// + /// deflater instance is null + /// + public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize) + { + if (baseOutputStream == null) + { + throw new ArgumentNullException("baseOutputStream"); + } + + if (baseOutputStream.CanWrite == false) + { + throw new ArgumentException("Must support writing", "baseOutputStream"); + } + + if (deflater == null) + { + throw new ArgumentNullException("deflater"); + } + + if (bufferSize < 512) + { + throw new ArgumentOutOfRangeException("bufferSize"); + } + + baseOutputStream_ = baseOutputStream; + buffer_ = new byte[bufferSize]; + deflater_ = deflater; + } + #endregion + + #region Public API + /// + /// Finishes the stream by calling finish() on the deflater. + /// + /// + /// Not all input is deflated + /// + public virtual void Finish() + { + deflater_.Finish(); + while (!deflater_.IsFinished) + { + int len = deflater_.Deflate(buffer_, 0, buffer_.Length); + if (len <= 0) + { + break; + } + +#if true//NETCF_1_0 + if (keys != null) + { +#else + if (cryptoTransform_ != null) { +#endif + EncryptBlock(buffer_, 0, len); + } + + baseOutputStream_.Write(buffer_, 0, len); + } + + if (!deflater_.IsFinished) + { + throw new SharpZipBaseException("Can't deflate all input?"); + } + + baseOutputStream_.Flush(); + +#if true//NETCF_1_0 + if (keys != null) + { + keys = null; + } +#else + if (cryptoTransform_ != null) { +#if !NET_1_1 && !NETCF_2_0 + if (cryptoTransform_ is ZipAESTransform) { + AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); + } +#endif + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } +#endif + } + + /// + /// Get/set flag indicating ownership of the underlying stream. + /// When the flag is true will close the underlying stream also. + /// + public bool IsStreamOwner + { + get { return isStreamOwner_; } + set { isStreamOwner_ = value; } + } + + /// + /// Allows client to determine if an entry can be patched after its added + /// + public bool CanPatchEntries + { + get + { + return baseOutputStream_.CanSeek; + } + } + + #endregion + + #region Encryption + + string password; + +#if true//NETCF_1_0 + uint[] keys; +#else + ICryptoTransform cryptoTransform_; + + /// + /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream. + /// + protected byte[] AESAuthCode; +#endif + + /// + /// Get/set the password used for encryption. + /// + /// When set to null or if the password is empty no encryption is performed + public string Password + { + get + { + return password; + } + set + { + if ((value != null) && (value.Length == 0)) + { + password = null; + } + else + { + password = value; + } + } + } + + /// + /// Encrypt a block of data + /// + /// + /// Data to encrypt. NOTE the original contents of the buffer are lost + /// + /// + /// Offset of first byte in buffer to encrypt + /// + /// + /// Number of bytes in buffer to encrypt + /// + protected void EncryptBlock(byte[] buffer, int offset, int length) + { +#if true//NETCF_1_0 + for (int i = offset; i < offset + length; ++i) + { + byte oldbyte = buffer[i]; + buffer[i] ^= EncryptByte(); + UpdateKeys(oldbyte); + } +#else + cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0); +#endif + } + + /// + /// Initializes encryption keys based on given . + /// + /// The password. + protected void InitializePassword(string password) + { +#if true//NETCF_1_0 + keys = new uint[] { + 0x12345678, + 0x23456789, + 0x34567890 + }; + + byte[] rawPassword = ZipConstants.ConvertToArray(password); + + for (int i = 0; i < rawPassword.Length; ++i) + { + UpdateKeys((byte)rawPassword[i]); + } + +#else + PkzipClassicManaged pkManaged = new PkzipClassicManaged(); + byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password)); + cryptoTransform_ = pkManaged.CreateEncryptor(key, null); +#endif + } + +#if false//!NET_1_1 && !NETCF_2_0 + /// + /// Initializes encryption keys based on given password. + /// + protected void InitializeAESPassword(ZipEntry entry, string rawPassword, + out byte[] salt, out byte[] pwdVerifier) { + salt = new byte[entry.AESSaltLen]; + // Salt needs to be cryptographically random, and unique per file + if (_aesRnd == null) + _aesRnd = new RNGCryptoServiceProvider(); + _aesRnd.GetBytes(salt); + int blockSize = entry.AESKeySize / 8; // bits to bytes + + cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true); + pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier; + } +#endif + +#if true//NETCF_1_0 + + /// + /// Encrypt a single byte + /// + /// + /// The encrypted value + /// + protected byte EncryptByte() + { + uint temp = ((keys[2] & 0xFFFF) | 2); + return (byte)((temp * (temp ^ 1)) >> 8); + } + + /// + /// Update encryption keys + /// + protected void UpdateKeys(byte ch) + { + keys[0] = Crc32.ComputeCrc32(keys[0], ch); + keys[1] = keys[1] + (byte)keys[0]; + keys[1] = keys[1] * 134775813 + 1; + keys[2] = Crc32.ComputeCrc32(keys[2], (byte)(keys[1] >> 24)); + } +#endif + + #endregion + + #region Deflation Support + /// + /// Deflates everything in the input buffers. This will call + /// def.deflate() until all bytes from the input buffers + /// are processed. + /// + protected void Deflate() + { + while (!deflater_.IsNeedingInput) + { + int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length); + + if (deflateCount <= 0) + { + break; + } +#if true//NETCF_1_0 + if (keys != null) +#else + if (cryptoTransform_ != null) +#endif + { + EncryptBlock(buffer_, 0, deflateCount); + } + + baseOutputStream_.Write(buffer_, 0, deflateCount); + } + + if (!deflater_.IsNeedingInput) + { + throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?"); + } + } + #endregion + + #region Stream Overrides + /// + /// Gets value indicating stream can be read from + /// + public override bool CanRead + { + get + { + return false; + } + } + + /// + /// Gets a value indicating if seeking is supported for this stream + /// This property always returns false + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Get value indicating if this stream supports writing + /// + public override bool CanWrite + { + get + { + return baseOutputStream_.CanWrite; + } + } + + /// + /// Get current length of stream + /// + public override long Length + { + get + { + return baseOutputStream_.Length; + } + } + + /// + /// Gets the current position within the stream. + /// + /// Any attempt to set position + public override long Position + { + get + { + return baseOutputStream_.Position; + } + set + { + throw new NotSupportedException("Position property not supported"); + } + } + + /// + /// Sets the current position of this stream to the given value. Not supported by this class! + /// + /// The offset relative to the to seek. + /// The to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("DeflaterOutputStream Seek not supported"); + } + + /// + /// Sets the length of this stream to the given value. Not supported by this class! + /// + /// The new stream length. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("DeflaterOutputStream SetLength not supported"); + } + + /// + /// Read a byte from stream advancing position by one + /// + /// The byte read cast to an int. THe value is -1 if at the end of the stream. + /// Any access + public override int ReadByte() + { + throw new NotSupportedException("DeflaterOutputStream ReadByte not supported"); + } + + /// + /// Read a block of bytes from stream + /// + /// The buffer to store read data in. + /// The offset to start storing at. + /// The maximum number of bytes to read. + /// The actual number of bytes read. Zero if end of stream is detected. + /// Any access + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("DeflaterOutputStream Read not supported"); + } + +#if !NETFX_CORE && !UWP && !DNC10 + /// + /// Asynchronous reads are not supported a NotSupportedException is always thrown + /// + /// The buffer to read into. + /// The offset to start storing data at. + /// The number of bytes to read + /// The async callback to use. + /// The state to use. + /// Returns an + /// Any access + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException("DeflaterOutputStream BeginRead not currently supported"); + } +#endif + +#if !NETFX_CORE && !UWP && !DNC10 + /// + /// Asynchronous writes arent supported, a NotSupportedException is always thrown + /// + /// The buffer to write. + /// The offset to begin writing at. + /// The number of bytes to write. + /// The to use. + /// The state object. + /// Returns an IAsyncResult. + /// Any access + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException("BeginWrite is not supported"); + } +#endif + + /// + /// Flushes the stream by calling Flush on the deflater and then + /// on the underlying stream. This ensures that all bytes are flushed. + /// + public override void Flush() + { + deflater_.Flush(); + Deflate(); + baseOutputStream_.Flush(); + } + +#if !NETFX_CORE && !UWP && !DNC10 + /// + /// Calls and closes the underlying + /// stream when is true. + /// + public override void Close() + { + if ( !isClosed_ ) { + isClosed_ = true; + + try { + Finish(); +#if true//NETCF_1_0 + keys =null; +#else + if ( cryptoTransform_ != null ) { + GetAuthCodeIfAES(); + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } +#endif + } + finally { + if( isStreamOwner_ ) { + baseOutputStream_.Close(); + } + } + } + } +#else + public void Close() + { + if (!isClosed_) + { + isClosed_ = true; + + try + { + Finish(); +#if true//NETCF_1_0 + keys = null; +#else + if ( cryptoTransform_ != null ) { + GetAuthCodeIfAES(); + cryptoTransform_.Dispose(); + cryptoTransform_ = null; + } +#endif + } + finally + { + if (isStreamOwner_) + { + //baseOutputStream_.Close(); + baseOutputStream_.Dispose(); + } + } + } + } +#endif + + + private void GetAuthCodeIfAES() + { +#if false//!NET_1_1 && !NETCF_2_0 + if (cryptoTransform_ is ZipAESTransform) { + AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode(); + } +#endif + } + + /// + /// Writes a single byte to the compressed output stream. + /// + /// + /// The byte value. + /// + public override void WriteByte(byte value) + { + byte[] b = new byte[1]; + b[0] = value; + Write(b, 0, 1); + } + + /// + /// Writes bytes from an array to the compressed stream. + /// + /// + /// The byte array + /// + /// + /// The offset into the byte array where to start. + /// + /// + /// The number of bytes to write. + /// + public override void Write(byte[] buffer, int offset, int count) + { + deflater_.SetInput(buffer, offset, count); + Deflate(); + } + #endregion + + #region Instance Fields + /// + /// This buffer is used temporarily to retrieve the bytes from the + /// deflater and write them to the underlying output stream. + /// + byte[] buffer_; + + /// + /// The deflater which is used to deflate the stream. + /// + protected Deflater deflater_; + + /// + /// Base stream the deflater depends on. + /// + protected Stream baseOutputStream_; + +#if true || !NETFX_CORE && !UWP + bool isClosed_; +#endif + + bool isStreamOwner_ = true; + #endregion + + #region Static Fields + +#if false//!NET_1_1 && !NETCF_2_0 + // Static to help ensure that multiple files within a zip will get different random salt + private static RNGCryptoServiceProvider _aesRnd; +#endif + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs new file mode 100644 index 00000000..baf265d3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/InflaterInputStream.cs @@ -0,0 +1,811 @@ +// InflaterInputStream.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +// HISTORY +// 11-08-2009 GeoffHart T9121 Added Multi-member gzip support + +using System; +using System.IO; + +// ReSharper disable RedundantThisQualifier + +#if false//!NETCF_1_0 +using System.Security.Cryptography; +#endif + +namespace PdfSharp.SharpZipLib.Zip.Compression.Streams +{ + + /// + /// An input buffer customised for use by + /// + /// + /// The buffer supports decryption of incoming data. + /// + internal class InflaterInputBuffer + { + #region Constructors + /// + /// Initialise a new instance of with a default buffer size + /// + /// The stream to buffer. + public InflaterInputBuffer(Stream stream) + : this(stream, 4096) + { + } + + /// + /// Initialise a new instance of + /// + /// The stream to buffer. + /// The size to use for the buffer + /// A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB. + public InflaterInputBuffer(Stream stream, int bufferSize) + { + inputStream = stream; + if (bufferSize < 1024) + { + bufferSize = 1024; + } + rawData = new byte[bufferSize]; + clearText = rawData; + } + #endregion + + /// + /// Get the length of bytes bytes in the + /// + public int RawLength + { + get + { + return rawLength; + } + } + + /// + /// Get the contents of the raw data buffer. + /// + /// This may contain encrypted data. + public byte[] RawData + { + get + { + return rawData; + } + } + + /// + /// Get the number of useable bytes in + /// + public int ClearTextLength + { + get + { + return clearTextLength; + } + } + + /// + /// Get the contents of the clear text buffer. + /// + public byte[] ClearText + { + get + { + return clearText; + } + } + + /// + /// Get/set the number of bytes available + /// + public int Available + { + get { return available; } + set { available = value; } + } + + /// + /// Call passing the current clear text buffer contents. + /// + /// The inflater to set input for. + public void SetInflaterInput(Inflater inflater) + { + if (available > 0) + { + inflater.SetInput(clearText, clearTextLength - available, available); + available = 0; + } + } + + /// + /// Fill the buffer from the underlying input stream. + /// + public void Fill() + { + rawLength = 0; + int toRead = rawData.Length; + + while (toRead > 0) + { + int count = inputStream.Read(rawData, rawLength, toRead); + if (count <= 0) + { + break; + } + rawLength += count; + toRead -= count; + } + +#if false//!NETCF_1_0 + if ( cryptoTransform != null ) { + clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0); + } + else +#endif + { + clearTextLength = rawLength; + } + + available = clearTextLength; + } + + /// + /// Read a buffer directly from the input stream + /// + /// The buffer to fill + /// Returns the number of bytes read. + public int ReadRawBuffer(byte[] buffer) + { + return ReadRawBuffer(buffer, 0, buffer.Length); + } + + /// + /// Read a buffer directly from the input stream + /// + /// The buffer to read into + /// The offset to start reading data into. + /// The number of bytes to read. + /// Returns the number of bytes read. + public int ReadRawBuffer(byte[] outBuffer, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException("length"); + } + + int currentOffset = offset; + int currentLength = length; + + while (currentLength > 0) + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + return 0; + } + } + int toCopy = Math.Min(currentLength, available); + System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy); + currentOffset += toCopy; + currentLength -= toCopy; + available -= toCopy; + } + return length; + } + + /// + /// Read clear text data from the input stream. + /// + /// The buffer to add data to. + /// The offset to start adding data at. + /// The number of bytes to read. + /// Returns the number of bytes actually read. + public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException("length"); + } + + int currentOffset = offset; + int currentLength = length; + + while (currentLength > 0) + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + return 0; + } + } + + int toCopy = Math.Min(currentLength, available); + Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy); + currentOffset += toCopy; + currentLength -= toCopy; + available -= toCopy; + } + return length; + } + + /// + /// Read a from the input stream. + /// + /// Returns the byte read. + public int ReadLeByte() + { + if (available <= 0) + { + Fill(); + if (available <= 0) + { + throw new ZipException("EOF in header"); + } + } + byte result = rawData[rawLength - available]; + available -= 1; + return result; + } + + /// + /// Read an in little endian byte order. + /// + /// The short value read case to an int. + public int ReadLeShort() + { + return ReadLeByte() | (ReadLeByte() << 8); + } + + /// + /// Read an in little endian byte order. + /// + /// The int value read. + public int ReadLeInt() + { + return ReadLeShort() | (ReadLeShort() << 16); + } + + /// + /// Read a in little endian byte order. + /// + /// The long value read. + public long ReadLeLong() + { + return (uint)ReadLeInt() | ((long)ReadLeInt() << 32); + } + +#if false//!NETCF_1_0 + /// + /// Get/set the to apply to any data. + /// + /// Set this value to null to have no transform applied. + public ICryptoTransform CryptoTransform + { + set { + cryptoTransform = value; + if ( cryptoTransform != null ) { + if ( rawData == clearText ) { + if ( internalClearText == null ) { + internalClearText = new byte[rawData.Length]; + } + clearText = internalClearText; + } + clearTextLength = rawLength; + if ( available > 0 ) { + cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available); + } + } else { + clearText = rawData; + clearTextLength = rawLength; + } + } + } +#endif + + #region Instance Fields + int rawLength; + byte[] rawData; + + int clearTextLength; + byte[] clearText; +#if false//!NETCF_1_0 + byte[] internalClearText; +#endif + + int available; + +#if false//!NETCF_1_0 + ICryptoTransform cryptoTransform; +#endif + Stream inputStream; + #endregion + } + + /// + /// This filter stream is used to decompress data compressed using the "deflate" + /// format. The "deflate" format is described in RFC 1951. + /// + /// This stream may form the basis for other decompression filters, such + /// as the GZipInputStream. + /// + /// Author of the original java version: John Leuner. + /// + internal class InflaterInputStream : Stream + { + #region Constructors + /// + /// Create an InflaterInputStream with the default decompressor + /// and a default buffer size of 4KB. + /// + /// + /// The InputStream to read bytes from + /// + public InflaterInputStream(Stream baseInputStream) + : this(baseInputStream, new Inflater(), 4096) + { + } + + /// + /// Create an InflaterInputStream with the specified decompressor + /// and a default buffer size of 4KB. + /// + /// + /// The source of input data + /// + /// + /// The decompressor used to decompress data read from baseInputStream + /// + public InflaterInputStream(Stream baseInputStream, Inflater inf) + : this(baseInputStream, inf, 4096) + { + } + + /// + /// Create an InflaterInputStream with the specified decompressor + /// and the specified buffer size. + /// + /// + /// The InputStream to read bytes from + /// + /// + /// The decompressor to use + /// + /// + /// Size of the buffer to use + /// + public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize) + { + if (baseInputStream == null) + { + throw new ArgumentNullException("baseInputStream"); + } + + if (inflater == null) + { + throw new ArgumentNullException("inflater"); + } + + if (bufferSize <= 0) + { + throw new ArgumentOutOfRangeException("bufferSize"); + } + + this.baseInputStream = baseInputStream; + this.inf = inflater; + + inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize); + } + + #endregion + + /// + /// Get/set flag indicating ownership of underlying stream. + /// When the flag is true will close the underlying stream also. + /// + /// + /// The default value is true. + /// + public bool IsStreamOwner + { + get { return isStreamOwner; } + set { isStreamOwner = value; } + } + + /// + /// Skip specified number of bytes of uncompressed data + /// + /// + /// Number of bytes to skip + /// + /// + /// The number of bytes skipped, zero if the end of + /// stream has been reached + /// + /// + /// The number of bytes to skip is less than or equal to zero. + /// + public long Skip(long count) + { + if (count <= 0) + { + throw new ArgumentOutOfRangeException("count"); + } + + // v0.80 Skip by seeking if underlying stream supports it... + if (baseInputStream.CanSeek) + { + baseInputStream.Seek(count, SeekOrigin.Current); + return count; + } + else + { + int length = 2048; + if (count < length) + { + length = (int)count; + } + + byte[] tmp = new byte[length]; + int readCount = 1; + long toSkip = count; + + while ((toSkip > 0) && (readCount > 0)) + { + if (toSkip < length) + { + length = (int)toSkip; + } + + readCount = baseInputStream.Read(tmp, 0, length); + toSkip -= readCount; + } + + return count - toSkip; + } + } + + /// + /// Clear any cryptographic state. + /// + protected void StopDecrypting() + { +#if false//!NETCF_1_0 + inputBuffer.CryptoTransform = null; +#endif + } + + /// + /// Returns 0 once the end of the stream (EOF) has been reached. + /// Otherwise returns 1. + /// + public virtual int Available + { + get + { + return inf.IsFinished ? 0 : 1; + } + } + + /// + /// Fills the buffer with more data to decompress. + /// + /// + /// Stream ends early + /// + protected void Fill() + { + // Protect against redundant calls + if (inputBuffer.Available <= 0) + { + inputBuffer.Fill(); + if (inputBuffer.Available <= 0) + { + throw new SharpZipBaseException("Unexpected EOF"); + } + } + inputBuffer.SetInflaterInput(inf); + } + + #region Stream Overrides + /// + /// Gets a value indicating whether the current stream supports reading + /// + public override bool CanRead + { + get + { + return baseInputStream.CanRead; + } + } + + /// + /// Gets a value of false indicating seeking is not supported for this stream. + /// + public override bool CanSeek + { + get + { + return false; + } + } + + /// + /// Gets a value of false indicating that this stream is not writeable. + /// + public override bool CanWrite + { + get + { + return false; + } + } + + /// + /// A value representing the length of the stream in bytes. + /// + public override long Length + { + get + { + return inputBuffer.RawLength; + } + } + + /// + /// The current position within the stream. + /// Throws a NotSupportedException when attempting to set the position + /// + /// Attempting to set the position + public override long Position + { + get + { + return baseInputStream.Position; + } + set + { + throw new NotSupportedException("InflaterInputStream Position not supported"); + } + } + + /// + /// Flushes the baseInputStream + /// + public override void Flush() + { + baseInputStream.Flush(); + } + + /// + /// Sets the position within the current stream + /// Always throws a NotSupportedException + /// + /// The relative offset to seek to. + /// The defining where to seek from. + /// The new position in the stream. + /// Any access + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Seek not supported"); + } + + /// + /// Set the length of the current stream + /// Always throws a NotSupportedException + /// + /// The new length value for the stream. + /// Any access + public override void SetLength(long value) + { + throw new NotSupportedException("InflaterInputStream SetLength not supported"); + } + + /// + /// Writes a sequence of bytes to stream and advances the current position + /// This method always throws a NotSupportedException + /// + /// Thew buffer containing data to write. + /// The offset of the first byte to write. + /// The number of bytes to write. + /// Any access + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("InflaterInputStream Write not supported"); + } + + /// + /// Writes one byte to the current stream and advances the current position + /// Always throws a NotSupportedException + /// + /// The byte to write. + /// Any access + public override void WriteByte(byte value) + { + throw new NotSupportedException("InflaterInputStream WriteByte not supported"); + } + +#if !NETFX_CORE && !UWP && !DNC10 + /// + /// Entry point to begin an asynchronous write. Always throws a NotSupportedException. + /// + /// The buffer to write data from + /// Offset of first byte to write + /// The maximum number of bytes to write + /// The method to be called when the asynchronous write operation is completed + /// A user-provided object that distinguishes this particular asynchronous write request from other requests + /// An IAsyncResult that references the asynchronous write + /// Any access + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + throw new NotSupportedException("InflaterInputStream BeginWrite not supported"); + } +#endif + +#if !NETFX_CORE && !UWP && !DNC10 + /// + /// Closes the input stream. When + /// is true the underlying stream is also closed. + /// + public override void Close() + { + if (!isClosed) + { + isClosed = true; + if (isStreamOwner) + { + baseInputStream.Close(); + } + } + } +#else + public void Close() + { + if (!isClosed) + { + isClosed = true; + if (isStreamOwner) + { + //baseInputStream.Close(); + baseInputStream.Dispose(); + } + } + } +#endif + + /// + /// Reads decompressed data into the provided buffer byte array + /// + /// + /// The array to read and decompress data into + /// + /// + /// The offset indicating where the data should be placed + /// + /// + /// The number of bytes to decompress + /// + /// The number of bytes read. Zero signals the end of stream + /// + /// Inflater needs a dictionary + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (inf.IsNeedingDictionary) + { + throw new SharpZipBaseException("Need a dictionary"); + } + + int remainingBytes = count; + while (true) + { + int bytesRead = inf.Inflate(buffer, offset, remainingBytes); + offset += bytesRead; + remainingBytes -= bytesRead; + + if (remainingBytes == 0 || inf.IsFinished) + { + break; + } + + if (inf.IsNeedingInput) + { + try + { + Fill(); + } + catch (SharpZipBaseException ex) + { + // Hack 16-05-25: Some PDF files lead to an "Unexpected EOF" exception. Is it safe to ignore this exception? + if (ex.Message != "Unexpected EOF") + throw; + // WB! early EOF: apparently not a big deal for some PDF pages: break out of the loop. + break; + } + } + else if (bytesRead == 0) + { + throw new ZipException("Don't know what to do"); + } + } + return count - remainingBytes; + } + #endregion + + #region Instance Fields + /// + /// Decompressor for this stream + /// + protected Inflater inf; + + /// + /// Input buffer for this stream. + /// + protected InflaterInputBuffer inputBuffer; + + /// + /// Base stream the inflater reads from. + /// + private Stream baseInputStream; + + ///// + ///// The compressed size + ///// + ////protected long csize; + +#if true || !NETFX_CORE + /// + /// Flag indicating wether this instance has been closed or not. + /// + bool isClosed; +#endif + + /// + /// Flag indicating wether this instance is designated the stream owner. + /// When closing if this flag is true the underlying stream is closed. + /// + bool isStreamOwner = true; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/OutputWindow.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/OutputWindow.cs new file mode 100644 index 00000000..697ad663 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/OutputWindow.cs @@ -0,0 +1,256 @@ +// OutputWindow.cs +// +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Zip.Compression.Streams +{ + + /// + /// Contains the output from the Inflation process. + /// We need to have a window so that we can refer backwards into the output stream + /// to repeat stuff.
+ /// Author of the original java version: John Leuner + ///
+ internal class OutputWindow + { + #region Constants + const int WindowSize = 1 << 15; + const int WindowMask = WindowSize - 1; + #endregion + + #region Instance Fields + byte[] window = new byte[WindowSize]; //The window is 2^15 bytes + int windowEnd; + int windowFilled; + #endregion + + /// + /// Write a byte to this output window + /// + /// value to write + /// + /// if window is full + /// + public void Write(int value) + { + if (windowFilled++ == WindowSize) + { + throw new InvalidOperationException("Window full"); + } + window[windowEnd++] = (byte)value; + windowEnd &= WindowMask; + } + + + private void SlowRepeat(int repStart, int length, int distance) + { + while (length-- > 0) + { + window[windowEnd++] = window[repStart++]; + windowEnd &= WindowMask; + repStart &= WindowMask; + } + } + + /// + /// Append a byte pattern already in the window itself + /// + /// length of pattern to copy + /// distance from end of window pattern occurs + /// + /// If the repeated data overflows the window + /// + public void Repeat(int length, int distance) + { + if ((windowFilled += length) > WindowSize) + { + throw new InvalidOperationException("Window full"); + } + + int repStart = (windowEnd - distance) & WindowMask; + int border = WindowSize - length; + if ((repStart <= border) && (windowEnd < border)) + { + if (length <= distance) + { + System.Array.Copy(window, repStart, window, windowEnd, length); + windowEnd += length; + } + else + { + // We have to copy manually, since the repeat pattern overlaps. + while (length-- > 0) + { + window[windowEnd++] = window[repStart++]; + } + } + } + else + { + SlowRepeat(repStart, length, distance); + } + } + + /// + /// Copy from input manipulator to internal window + /// + /// source of data + /// length of data to copy + /// the number of bytes copied + public int CopyStored(StreamManipulator input, int length) + { + length = Math.Min(Math.Min(length, WindowSize - windowFilled), input.AvailableBytes); + int copied; + + int tailLen = WindowSize - windowEnd; + if (length > tailLen) + { + copied = input.CopyBytes(window, windowEnd, tailLen); + if (copied == tailLen) + { + copied += input.CopyBytes(window, 0, length - tailLen); + } + } + else + { + copied = input.CopyBytes(window, windowEnd, length); + } + + windowEnd = (windowEnd + copied) & WindowMask; + windowFilled += copied; + return copied; + } + + /// + /// Copy dictionary to window + /// + /// source dictionary + /// offset of start in source dictionary + /// length of dictionary + /// + /// If window isnt empty + /// + public void CopyDict(byte[] dictionary, int offset, int length) + { + if (dictionary == null) + { + throw new ArgumentNullException("dictionary"); + } + + if (windowFilled > 0) + { + throw new InvalidOperationException(); + } + + if (length > WindowSize) + { + offset += length - WindowSize; + length = WindowSize; + } + System.Array.Copy(dictionary, offset, window, 0, length); + windowEnd = length & WindowMask; + } + + /// + /// Get remaining unfilled space in window + /// + /// Number of bytes left in window + public int GetFreeSpace() + { + return WindowSize - windowFilled; + } + + /// + /// Get bytes available for output in window + /// + /// Number of bytes filled + public int GetAvailable() + { + return windowFilled; + } + + /// + /// Copy contents of window to output + /// + /// buffer to copy to + /// offset to start at + /// number of bytes to count + /// The number of bytes copied + /// + /// If a window underflow occurs + /// + public int CopyOutput(byte[] output, int offset, int len) + { + int copyEnd = windowEnd; + if (len > windowFilled) + { + len = windowFilled; + } + else + { + copyEnd = (windowEnd - windowFilled + len) & WindowMask; + } + + int copied = len; + int tailLen = len - copyEnd; + + if (tailLen > 0) + { + System.Array.Copy(window, WindowSize - tailLen, output, offset, tailLen); + offset += tailLen; + len = copyEnd; + } + System.Array.Copy(window, copyEnd - len, output, offset, len); + windowFilled -= copied; + if (windowFilled < 0) + { + throw new InvalidOperationException(); + } + return copied; + } + + /// + /// Reset by clearing window so GetAvailable returns 0 + /// + public void Reset() + { + windowFilled = windowEnd = 0; + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs new file mode 100644 index 00000000..302e23d3 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/Compression/Streams/StreamManipulator.cs @@ -0,0 +1,318 @@ +// StreamManipulator.cs +// +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +namespace PdfSharp.SharpZipLib.Zip.Compression.Streams +{ + + /// + /// This class allows us to retrieve a specified number of bits from + /// the input buffer, as well as copy big byte blocks. + /// + /// It uses an int buffer to store up to 31 bits for direct + /// manipulation. This guarantees that we can get at least 16 bits, + /// but we only need at most 15, so this is all safe. + /// + /// There are some optimizations in this class, for example, you must + /// never peek more than 8 bits more than needed, and you must first + /// peek bits before you may drop them. This is not a general purpose + /// class but optimized for the behaviour of the Inflater. + /// + /// Authors of the original java version: John Leuner, Jochen Hoenicke + /// + internal class StreamManipulator + { + #region Constructors + /// + /// Constructs a default StreamManipulator with all buffers empty + /// + public StreamManipulator() + { + } + #endregion + + /// + /// Get the next sequence of bits but don't increase input pointer. bitCount must be + /// less or equal 16 and if this call succeeds, you must drop + /// at least n - 8 bits in the next call. + /// + /// The number of bits to peek. + /// + /// the value of the bits, or -1 if not enough bits available. */ + /// + public int PeekBits(int bitCount) + { + if (bitsInBuffer_ < bitCount) + { + if (windowStart_ == windowEnd_) + { + return -1; // ok + } + buffer_ |= (uint)((window_[windowStart_++] & 0xff | + (window_[windowStart_++] & 0xff) << 8) << bitsInBuffer_); + bitsInBuffer_ += 16; + } + return (int)(buffer_ & ((1 << bitCount) - 1)); + } + + /// + /// Drops the next n bits from the input. You should have called PeekBits + /// with a bigger or equal n before, to make sure that enough bits are in + /// the bit buffer. + /// + /// The number of bits to drop. + public void DropBits(int bitCount) + { + buffer_ >>= bitCount; + bitsInBuffer_ -= bitCount; + } + + /// + /// Gets the next n bits and increases input pointer. This is equivalent + /// to followed by , except for correct error handling. + /// + /// The number of bits to retrieve. + /// + /// the value of the bits, or -1 if not enough bits available. + /// + public int GetBits(int bitCount) + { + int bits = PeekBits(bitCount); + if (bits >= 0) + { + DropBits(bitCount); + } + return bits; + } + + /// + /// Gets the number of bits available in the bit buffer. This must be + /// only called when a previous PeekBits() returned -1. + /// + /// + /// the number of bits available. + /// + public int AvailableBits + { + get + { + return bitsInBuffer_; + } + } + + /// + /// Gets the number of bytes available. + /// + /// + /// The number of bytes available. + /// + public int AvailableBytes + { + get + { + return windowEnd_ - windowStart_ + (bitsInBuffer_ >> 3); + } + } + + /// + /// Skips to the next byte boundary. + /// + public void SkipToByteBoundary() + { + buffer_ >>= (bitsInBuffer_ & 7); + bitsInBuffer_ &= ~7; + } + + /// + /// Returns true when SetInput can be called + /// + public bool IsNeedingInput + { + get + { + return windowStart_ == windowEnd_; + } + } + + /// + /// Copies bytes from input buffer to output buffer starting + /// at output[offset]. You have to make sure, that the buffer is + /// byte aligned. If not enough bytes are available, copies fewer + /// bytes. + /// + /// + /// The buffer to copy bytes to. + /// + /// + /// The offset in the buffer at which copying starts + /// + /// + /// The length to copy, 0 is allowed. + /// + /// + /// The number of bytes copied, 0 if no bytes were available. + /// + /// + /// Length is less than zero + /// + /// + /// Bit buffer isnt byte aligned + /// + public int CopyBytes(byte[] output, int offset, int length) + { + if (length < 0) + { + throw new ArgumentOutOfRangeException("length"); + } + + if ((bitsInBuffer_ & 7) != 0) + { + // bits_in_buffer may only be 0 or a multiple of 8 + throw new InvalidOperationException("Bit buffer is not byte aligned!"); + } + + int count = 0; + while ((bitsInBuffer_ > 0) && (length > 0)) + { + output[offset++] = (byte)buffer_; + buffer_ >>= 8; + bitsInBuffer_ -= 8; + length--; + count++; + } + + if (length == 0) + { + return count; + } + + int avail = windowEnd_ - windowStart_; + if (length > avail) + { + length = avail; + } + System.Array.Copy(window_, windowStart_, output, offset, length); + windowStart_ += length; + + if (((windowStart_ - windowEnd_) & 1) != 0) + { + // We always want an even number of bytes in input, see peekBits + buffer_ = (uint)(window_[windowStart_++] & 0xff); + bitsInBuffer_ = 8; + } + return count + length; + } + + /// + /// Resets state and empties internal buffers + /// + public void Reset() + { + buffer_ = 0; + windowStart_ = windowEnd_ = bitsInBuffer_ = 0; + } + + /// + /// Add more input for consumption. + /// Only call when IsNeedingInput returns true + /// + /// data to be input + /// offset of first byte of input + /// number of bytes of input to add. + public void SetInput(byte[] buffer, int offset, int count) + { + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + if (offset < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("offset"); +#else + throw new ArgumentOutOfRangeException("offset", "Cannot be negative"); +#endif + } + + if (count < 0) + { +#if NETCF_1_0 + throw new ArgumentOutOfRangeException("count"); +#else + throw new ArgumentOutOfRangeException("count", "Cannot be negative"); +#endif + } + + if (windowStart_ < windowEnd_) + { + throw new InvalidOperationException("Old input was not completely processed"); + } + + int end = offset + count; + + // We want to throw an ArrayIndexOutOfBoundsException early. + // Note the check also handles integer wrap around. + if ((offset > end) || (end > buffer.Length)) + { + throw new ArgumentOutOfRangeException("count"); + } + + if ((count & 1) != 0) + { + // We always want an even number of bytes in input, see PeekBits + buffer_ |= (uint)((buffer[offset++] & 0xff) << bitsInBuffer_); + bitsInBuffer_ += 8; + } + + window_ = buffer; + windowStart_ = offset; + windowEnd_ = end; + } + + #region Instance Fields + private byte[] window_; + private int windowStart_; + private int windowEnd_; + + private uint buffer_; + private int bitsInBuffer_; + #endregion + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipConstants.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipConstants.cs new file mode 100644 index 00000000..5024aee9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipConstants.cs @@ -0,0 +1,678 @@ +// ZipConstants.cs +// +// Copyright (C) 2001 Mike Krueger +// Copyright (C) 2004 John Reilly +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +// HISTORY +// 22-12-2009 DavidPierson Added AES support + +using System; +using System.Globalization; +using System.Text; +using System.Threading; + +#if NETCF_1_0 || NETCF_2_0 +using System.Globalization; +#endif + +namespace PdfSharp.SharpZipLib.Zip +{ + + #region Enumerations + + /// + /// Determines how entries are tested to see if they should use Zip64 extensions or not. + /// + public enum UseZip64 + { + /// + /// Zip64 will not be forced on entries during processing. + /// + /// An entry can have this overridden if required ZipEntry.ForceZip64" + Off, + /// + /// Zip64 should always be used. + /// + On, + /// + /// #ZipLib will determine use based on entry values when added to archive. + /// + Dynamic, + } + + /// + /// The kind of compression used for an entry in an archive + /// + public enum CompressionMethod + { + /// + /// A direct copy of the file contents is held in the archive + /// + Stored = 0, + + /// + /// Common Zip compression method using a sliding dictionary + /// of up to 32KB and secondary compression from Huffman/Shannon-Fano trees + /// + Deflated = 8, + + /// + /// An extension to deflate with a 64KB window. Not supported by #Zip currently + /// + Deflate64 = 9, + + /// + /// BZip2 compression. Not supported by #Zip. + /// + BZip2 = 11, + + /// + /// WinZip special for AES encryption, Now supported by #Zip. + /// + WinZipAES = 99, + + } + + /// + /// Identifies the encryption algorithm used for an entry + /// + public enum EncryptionAlgorithm + { + /// + /// No encryption has been used. + /// + None = 0, + /// + /// Encrypted using PKZIP 2.0 or 'classic' encryption. + /// + PkzipClassic = 1, + /// + /// DES encryption has been used. + /// + Des = 0x6601, + /// + /// RC2 encryption has been used for encryption. + /// + RC2 = 0x6602, + /// + /// Triple DES encryption with 168 bit keys has been used for this entry. + /// + TripleDes168 = 0x6603, + /// + /// Triple DES with 112 bit keys has been used for this entry. + /// + TripleDes112 = 0x6609, + /// + /// AES 128 has been used for encryption. + /// + Aes128 = 0x660e, + /// + /// AES 192 has been used for encryption. + /// + Aes192 = 0x660f, + /// + /// AES 256 has been used for encryption. + /// + Aes256 = 0x6610, + /// + /// RC2 corrected has been used for encryption. + /// + RC2Corrected = 0x6702, + /// + /// Blowfish has been used for encryption. + /// + Blowfish = 0x6720, + /// + /// Twofish has been used for encryption. + /// + Twofish = 0x6721, + /// + /// RC4 has been used for encryption. + /// + RC4 = 0x6801, + /// + /// An unknown algorithm has been used for encryption. + /// + Unknown = 0xffff + } + + /// + /// Defines the contents of the general bit flags field for an archive entry. + /// + [Flags] + public enum GeneralBitFlags : int + { + /// + /// Bit 0 if set indicates that the file is encrypted + /// + Encrypted = 0x0001, + /// + /// Bits 1 and 2 - Two bits defining the compression method (only for Method 6 Imploding and 8,9 Deflating) + /// + Method = 0x0006, + /// + /// Bit 3 if set indicates a trailing data desciptor is appended to the entry data + /// + Descriptor = 0x0008, + /// + /// Bit 4 is reserved for use with method 8 for enhanced deflation + /// + ReservedPKware4 = 0x0010, + /// + /// Bit 5 if set indicates the file contains Pkzip compressed patched data. + /// Requires version 2.7 or greater. + /// + Patched = 0x0020, + /// + /// Bit 6 if set indicates strong encryption has been used for this entry. + /// + StrongEncryption = 0x0040, + /// + /// Bit 7 is currently unused + /// + Unused7 = 0x0080, + /// + /// Bit 8 is currently unused + /// + Unused8 = 0x0100, + /// + /// Bit 9 is currently unused + /// + Unused9 = 0x0200, + /// + /// Bit 10 is currently unused + /// + Unused10 = 0x0400, + /// + /// Bit 11 if set indicates the filename and + /// comment fields for this file must be encoded using UTF-8. + /// + UnicodeText = 0x0800, + /// + /// Bit 12 is documented as being reserved by PKware for enhanced compression. + /// + EnhancedCompress = 0x1000, + /// + /// Bit 13 if set indicates that values in the local header are masked to hide + /// their actual values, and the central directory is encrypted. + /// + /// + /// Used when encrypting the central directory contents. + /// + HeaderMasked = 0x2000, + /// + /// Bit 14 is documented as being reserved for use by PKware + /// + ReservedPkware14 = 0x4000, + /// + /// Bit 15 is documented as being reserved for use by PKware + /// + ReservedPkware15 = 0x8000 + } + + #endregion + + /// + /// This class contains constants used for Zip format files + /// + internal sealed class ZipConstants + { + #region Versions + /// + /// The version made by field for entries in the central header when created by this library + /// + /// + /// This is also the Zip version for the library when comparing against the version required to extract + /// for an entry. See ZipEntry.CanDecompress. + /// + public const int VersionMadeBy = 51; // was 45 before AES + + /// + /// The version made by field for entries in the central header when created by this library + /// + /// + /// This is also the Zip version for the library when comparing against the version required to extract + /// for an entry. See ZipInputStream.CanDecompressEntry. + /// + [Obsolete("Use VersionMadeBy instead")] + public const int VERSION_MADE_BY = 51; + + /// + /// The minimum version required to support strong encryption + /// + public const int VersionStrongEncryption = 50; + + /// + /// The minimum version required to support strong encryption + /// + [Obsolete("Use VersionStrongEncryption instead")] + public const int VERSION_STRONG_ENCRYPTION = 50; + + /// + /// Version indicating AES encryption + /// + public const int VERSION_AES = 51; + + /// + /// The version required for Zip64 extensions (4.5 or higher) + /// + public const int VersionZip64 = 45; + #endregion + + #region Header Sizes + /// + /// Size of local entry header (excluding variable length fields at end) + /// + public const int LocalHeaderBaseSize = 30; + + /// + /// Size of local entry header (excluding variable length fields at end) + /// + [Obsolete("Use LocalHeaderBaseSize instead")] + public const int LOCHDR = 30; + + /// + /// Size of Zip64 data descriptor + /// + public const int Zip64DataDescriptorSize = 24; + + /// + /// Size of data descriptor + /// + public const int DataDescriptorSize = 16; + + /// + /// Size of data descriptor + /// + [Obsolete("Use DataDescriptorSize instead")] + public const int EXTHDR = 16; + + /// + /// Size of central header entry (excluding variable fields) + /// + public const int CentralHeaderBaseSize = 46; + + /// + /// Size of central header entry + /// + [Obsolete("Use CentralHeaderBaseSize instead")] + public const int CENHDR = 46; + + /// + /// Size of end of central record (excluding variable fields) + /// + public const int EndOfCentralRecordBaseSize = 22; + + /// + /// Size of end of central record (excluding variable fields) + /// + [Obsolete("Use EndOfCentralRecordBaseSize instead")] + public const int ENDHDR = 22; + + /// + /// Size of 'classic' cryptographic header stored before any entry data + /// + public const int CryptoHeaderSize = 12; + + /// + /// Size of cryptographic header stored before entry data + /// + [Obsolete("Use CryptoHeaderSize instead")] + public const int CRYPTO_HEADER_SIZE = 12; + #endregion + + #region Header Signatures + + /// + /// Signature for local entry header + /// + public const int LocalHeaderSignature = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); + + /// + /// Signature for local entry header + /// + [Obsolete("Use LocalHeaderSignature instead")] + public const int LOCSIG = 'P' | ('K' << 8) | (3 << 16) | (4 << 24); + + /// + /// Signature for spanning entry + /// + public const int SpanningSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for spanning entry + /// + [Obsolete("Use SpanningSignature instead")] + public const int SPANNINGSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for temporary spanning entry + /// + public const int SpanningTempSignature = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); + + /// + /// Signature for temporary spanning entry + /// + [Obsolete("Use SpanningTempSignature instead")] + public const int SPANTEMPSIG = 'P' | ('K' << 8) | ('0' << 16) | ('0' << 24); + + /// + /// Signature for data descriptor + /// + /// + /// This is only used where the length, Crc, or compressed size isnt known when the + /// entry is created and the output stream doesnt support seeking. + /// The local entry cannot be 'patched' with the correct values in this case + /// so the values are recorded after the data prefixed by this header, as well as in the central directory. + /// + public const int DataDescriptorSignature = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for data descriptor + /// + /// + /// This is only used where the length, Crc, or compressed size isnt known when the + /// entry is created and the output stream doesnt support seeking. + /// The local entry cannot be 'patched' with the correct values in this case + /// so the values are recorded after the data prefixed by this header, as well as in the central directory. + /// + [Obsolete("Use DataDescriptorSignature instead")] + public const int EXTSIG = 'P' | ('K' << 8) | (7 << 16) | (8 << 24); + + /// + /// Signature for central header + /// + [Obsolete("Use CentralHeaderSignature instead")] + public const int CENSIG = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); + + /// + /// Signature for central header + /// + public const int CentralHeaderSignature = 'P' | ('K' << 8) | (1 << 16) | (2 << 24); + + /// + /// Signature for Zip64 central file header + /// + public const int Zip64CentralFileHeaderSignature = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); + + /// + /// Signature for Zip64 central file header + /// + [Obsolete("Use Zip64CentralFileHeaderSignature instead")] + public const int CENSIG64 = 'P' | ('K' << 8) | (6 << 16) | (6 << 24); + + /// + /// Signature for Zip64 central directory locator + /// + public const int Zip64CentralDirLocatorSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); + + /// + /// Signature for archive extra data signature (were headers are encrypted). + /// + public const int ArchiveExtraDataSignature = 'P' | ('K' << 8) | (6 << 16) | (7 << 24); + + /// + /// Central header digitial signature + /// + public const int CentralHeaderDigitalSignature = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); + + /// + /// Central header digitial signature + /// + [Obsolete("Use CentralHeaderDigitalSignaure instead")] + public const int CENDIGITALSIG = 'P' | ('K' << 8) | (5 << 16) | (5 << 24); + + /// + /// End of central directory record signature + /// + public const int EndOfCentralDirectorySignature = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); + + /// + /// End of central directory record signature + /// + [Obsolete("Use EndOfCentralDirectorySignature instead")] + public const int ENDSIG = 'P' | ('K' << 8) | (5 << 16) | (6 << 24); + #endregion + +#if true//NETCF_1_0 || NETCF_2_0 + // This isn't so great but is better than nothing. + // Trying to work out an appropriate OEM code page would be good. + // 850 is a good default for English speakers particularly in Europe. +#if SILVERLIGHT || NETFX_CORE || UWP || DNC10 + // TODO Do we need this for PDFsharp? If so, make it work. + static int defaultCodePage = 65001; +#else + static int defaultCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage; +#endif +#else + /// + /// Get OEM codepage from NetFX, which parses the NLP file with culture info table etc etc. + /// But sometimes it yields the special value of 1 which is nicknamed CodePageNoOEM in sources (might also mean CP_OEMCP, but Encoding puts it so). + /// This was observed on Ukranian and Hindu systems. + /// Given this value, throws an . + /// So replace it with some fallback, e.g. 437 which is the default codepage in a console in a default Windows installation. + /// + static int defaultCodePage = + // these values cause ArgumentException in subsequent calls to Encoding::GetEncoding() + ((Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 1) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 2) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 3) || (Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage == 42)) + ? 437 // The default OEM encoding in a console in a default Windows installation, as a fallback. + : Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage; +#endif + + /// + /// Default encoding used for string conversion. 0 gives the default system OEM code page. + /// Dont use unicode encodings if you want to be Zip compatible! + /// Using the default code page isnt the full solution necessarily + /// there are many variable factors, codepage 850 is often a good choice for + /// European users, however be careful about compatibility. + /// + public static int DefaultCodePage + { + get + { + return defaultCodePage; + } + set + { + if ((value < 0) || (value > 65535) || + (value == 1) || (value == 2) || (value == 3) || (value == 42)) + { + throw new ArgumentOutOfRangeException("value"); + } + + defaultCodePage = value; + } + } + + /// + /// Convert a portion of a byte array to a string. + /// + /// + /// Data to convert to string + /// + /// + /// Number of bytes to convert starting from index 0 + /// + /// + /// data[0]..data[count - 1] converted to a string + /// + public static string ConvertToString(byte[] data, int count) + { + if (data == null) + { + return string.Empty; + } + +#if SILVERLIGHT || NETFX_CORE + return Encoding.GetEncoding("utf-8").GetString(data, 0, count); +#else + return Encoding.GetEncoding(DefaultCodePage).GetString(data, 0, count); +#endif + } + + /// + /// Convert a byte array to string + /// + /// + /// Byte array to convert + /// + /// + /// dataconverted to a string + /// + public static string ConvertToString(byte[] data) + { + if (data == null) + { + return string.Empty; + } + return ConvertToString(data, data.Length); + } + + /// + /// Convert a byte array to string + /// + /// The applicable general purpose bits flags + /// + /// Byte array to convert + /// + /// The number of bytes to convert. + /// + /// dataconverted to a string + /// + public static string ConvertToStringExt(int flags, byte[] data, int count) + { + if (data == null) + { + return string.Empty; + } + + if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) + { + return Encoding.UTF8.GetString(data, 0, count); + } + else + { + return ConvertToString(data, count); + } + } + + /// + /// Convert a byte array to string + /// + /// + /// Byte array to convert + /// + /// The applicable general purpose bits flags + /// + /// dataconverted to a string + /// + public static string ConvertToStringExt(int flags, byte[] data) + { + if (data == null) + { + return string.Empty; + } + + if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) + { + return Encoding.UTF8.GetString(data, 0, data.Length); + } + else + { + return ConvertToString(data, data.Length); + } + } + + /// + /// Convert a string to a byte array + /// + /// + /// String to convert to an array + /// + /// Converted array + public static byte[] ConvertToArray(string str) + { + if (str == null) + { + return new byte[0]; + } + +#if SILVERLIGHT || NETFX_CORE + return Encoding.GetEncoding("utf-8").GetBytes(str); +#else + return Encoding.GetEncoding(DefaultCodePage).GetBytes(str); +#endif + } + + /// + /// Convert a string to a byte array + /// + /// The applicable general purpose bits flags + /// + /// String to convert to an array + /// + /// Converted array + public static byte[] ConvertToArray(int flags, string str) + { + if (str == null) + { + return new byte[0]; + } + + if ((flags & (int)GeneralBitFlags.UnicodeText) != 0) + { + return Encoding.UTF8.GetBytes(str); + } + else + { + return ConvertToArray(str); + } + } + + + /// + /// Initialise default instance of ZipConstants + /// + /// + /// Private to prevent instances being created. + /// + ZipConstants() + { + // Do nothing + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipException.cs b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipException.cs new file mode 100644 index 00000000..de2f0e9e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SharpZipLib/Zip/ZipException.cs @@ -0,0 +1,93 @@ +// ZipException.cs +// +// Copyright (C) 2001 Mike Krueger +// +// This file was translated from java, it was part of the GNU Classpath +// Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Linking this library statically or dynamically with other modules is +// making a combined work based on this library. Thus, the terms and +// conditions of the GNU General Public License cover the whole +// combination. +// +// As a special exception, the copyright holders of this library give you +// permission to link this library with independent modules to produce an +// executable, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting executable under +// terms of your choice, provided that you also meet, for each linked +// independent module, the terms and conditions of the license of that +// module. An independent module is a module which is not derived from +// or based on this library. If you modify this library, you may extend +// this exception to your version of the library, but you are not +// obligated to do so. If you do not wish to do so, delete this +// exception statement from your version. + +using System; + +#if false//!NETCF_1_0 && !NETCF_2_0 +using System.Runtime.Serialization; +#endif + +namespace PdfSharp.SharpZipLib.Zip +{ + /// + /// Represents exception conditions specific to Zip archive handling + /// +#if false//!NETCF_1_0 && !NETCF_2_0 + [Serializable] +#endif + internal class ZipException : SharpZipBaseException + { +#if false//!NETCF_1_0 && !NETCF_2_0 + /// + /// Deserialization constructor + /// + /// for this constructor + /// for this constructor + protected ZipException(SerializationInfo info, StreamingContext context ) + : base( info, context ) + { + } +#endif + + /// + /// Initializes a new instance of the ZipException class. + /// + public ZipException() + { + } + + /// + /// Initializes a new instance of the ZipException class with a specified error message. + /// + /// The error message that explains the reason for the exception. + public ZipException(string message) + : base(message) + { + } + + /// + /// Initialise a new instance of ZipException. + /// + /// A message describing the error. + /// The exception that is the cause of the current exception. + public ZipException(string message, Exception exception) + : base(message, exception) + { + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Silverlight/SilverlightHelper.cs b/src/PDFsharp/src/PdfSharp/Silverlight/SilverlightHelper.cs new file mode 100644 index 00000000..0cfd8296 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Silverlight/SilverlightHelper.cs @@ -0,0 +1,83 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.IsolatedStorage; +using System.Runtime.InteropServices; + +namespace PdfSharp.Silverlight +{ + /// + /// Useful stuff to show PDF files in Silverlight applications for + /// testing and debugging purposes. + /// Some functions require elevated trust. + /// + public class SilverlightHelper + { + /// + /// Gets the full path of UserStore for application. + /// + public static string FullPathOfUserStoreForApplication + { + get + { + if (_fullPathOfUserStoreForApplication != null) + return _fullPathOfUserStoreForApplication; + + // More simple than I expected... + string root = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\Microsoft\Silverlight\is"; + IsolatedStorageFile userStore = IsolatedStorageFile.GetUserStoreForApplication(); + string markerFile = Guid.NewGuid().ToString(); + userStore.CreateFile(markerFile).Close(); + // Won't use Linq here (FirstOrDefault). + IEnumerator enumerator = Directory.EnumerateFileSystemEntries(root, markerFile, SearchOption.AllDirectories).GetEnumerator(); + enumerator.MoveNext(); + _fullPathOfUserStoreForApplication = Path.GetDirectoryName(enumerator.Current); + userStore.DeleteFile(markerFile); + return _fullPathOfUserStoreForApplication; + } + } + static string _fullPathOfUserStoreForApplication; + + /// + /// Uses ShellExecute to open a PDF file in UserStore. + /// + public static void ShowPdfFileFromUserStore(string path) + { + string fullPath = Path.Combine(FullPathOfUserStoreForApplication, path); + ShellExecute(IntPtr.Zero, "open", fullPath, IntPtr.Zero, null, 5); + } + + [DllImport("Shell32.dll")] + static extern UInt32 ShellExecute(IntPtr hwnd, string pOperation, string pFile, + IntPtr pParameters, string pDirectory, UInt32 nShowCmd); + } +} diff --git a/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgDrawingContext.cs b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgDrawingContext.cs new file mode 100644 index 00000000..f251b16f --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgDrawingContext.cs @@ -0,0 +1,392 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if SILVERLIGHT +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Controls; +using System.Windows.Shapes; +using PdfSharp.Drawing; +using PdfSharp.Internal; + +// ReSharper disable ConvertPropertyToExpressionBody + +namespace System.Windows.Media +{ + /// + /// The WPF graphic system has the DrawingContext class that implements the low-level + /// primitives for retained drawing. The lowest level in Silverlight a some non-aggregated + /// UI elements like several Shape objects, TextBlock and Glyphs. + /// This Silverlight version of DrawingContext simplyfies the implementation of XGraphics. + /// It converts function calls to DrawingContext into UI elements layered on Canvas + /// objects. + /// + internal class AgDrawingContext + { + /// + /// Initializes a new instance of the class. + /// Drawing creates objects that are placed on the specified canvas. + /// + internal AgDrawingContext(Canvas canvas) + { + if (canvas == null) + throw new ArgumentNullException("canvas"); + + // The size of the canvas is not used and does not matter. + // The properties of the canvas are not modified. Instead a new + // canvas is added to its Children list. + _canvasStack.Push(canvas); + PushCanvas(); + } + + public void Close() + { + // There is nothing to close in Silverlight. + } + + // There are no drawing objects in Silverlight. + //public void DrawDrawing(Drawing drawing); + + public void DrawEllipse(Brush brush, Pen pen, Point center, double radiusX, double radiusY) + { + Ellipse ellipse = new Ellipse(); + SetupShape(ellipse, center.X - radiusX, center.Y - radiusY, radiusX * 2, radiusY * 2, brush, pen); + ellipse.Fill = brush; + ActiveCanvas.Children.Add(ellipse); + } + + //public void DrawEllipse(Brush brush, Pen pen, Point center, AnimationClock centerAnimations, double radiusX, AnimationClock radiusXAnimations, double radiusY, AnimationClock radiusYAnimations); + + public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) + { + Path path = new Path(); + SetupShape(path, 0, 0, Double.NaN, Double.NaN, brush, pen); + path.Data = geometry; + ActiveCanvas.Children.Add(path); + } + + public void DrawPath(Brush brush, Pen pen, Path path) + { + SetupShape(path, brush, pen); + ActiveCanvas.Children.Add(path); + } + + //public void DrawGlyphRun(Brush foregroundBrush, GlyphRun glyphRun); + + public void DrawImage(ImageSource imageSource, Rect rectangle) + { + // TODO + Image image = new Image(); + image.Source = imageSource; + Canvas.SetLeft(image, rectangle.X); + Canvas.SetTop(image, rectangle.Y); + image.Width = rectangle.Width; + image.Height = rectangle.Height; + + ActiveCanvas.Children.Add(image); + } + + public void DrawLine(Pen pen, Point point0, Point point1) + { +#if true + Line line = new Line(); + SetupShape(line, 0, 0, Double.NaN, Double.NaN, null, pen); + line.X1 = point0.X; + line.Y1 = point0.Y; + line.X2 = point1.X; + line.Y2 = point1.Y; + ActiveCanvas.Children.Add(line); +#else + Line line = new Line(); + double x = Math.Min(point0.X, point1.X); + double y = Math.Min(point0.Y, point1.Y); + + // Prevent clipping thick lines parallel to shape boundaries. + double delta = 0; // 2 * pen.Thickness; + //SetupShape(line, x - delta, y - delta, width + 2 * delta, height + 2 * delta, null, pen); + SetupShape(line, x - delta, y - delta, Double.NaN, Double.NaN, null, pen); + line.X1 = point0.X - x + delta; + line.Y1 = point0.Y - y + delta; + line.X2 = point1.X - x + delta; + line.Y2 = point1.Y - y + delta; + ActiveCanvas.Children.Add(line); +#endif + } + + public void DrawRectangle(Brush brush, Pen pen, Rect rect) + { + Rectangle rectangle = new Rectangle(); + SetupShape(rectangle, rect.X, rect.Y, rect.Width, rect.Height, brush, pen); + ActiveCanvas.Children.Add(rectangle); + } + + public void DrawRoundedRectangle(Brush brush, Pen pen, Rect rect, double radiusX, double radiusY) + { + Rectangle rectangle = new Rectangle(); + SetupShape(rectangle, rect.X, rect.Y, rect.Width, rect.Height, brush, pen); + rectangle.RadiusX = radiusX; + rectangle.RadiusY = radiusY; + ActiveCanvas.Children.Add(rectangle); + } + + static void SetupShape(Shape shape, double x, double y, double width, double height, Brush brush, Pen pen) + { + if (width < 0) // nan < 0 is false + { + x += width; + width = -width; + } + if (height < 0) + { + y += height; + height = -height; + } + Canvas.SetLeft(shape, x); + Canvas.SetTop(shape, y); + + // Setting a double dependency property to Double.NaN is not the same + // as simply not setting it. I consider this is a bug in the Silverlight run-time. + if (!DoubleUtil.IsNaN(width)) + shape.Width = width; + if (!DoubleUtil.IsNaN(height)) + shape.Height = height; + SetupShape(shape, brush, pen); + } + + static void SetupShape(Shape shape, Brush brush, Pen pen) + { + shape.Fill = brush; + if (pen != null) + { + DoubleCollection dashArray = new DoubleCollection(); + foreach (double value in pen.DashArray) + dashArray.Add(value); + + shape.Stroke = pen.Brush; + shape.StrokeThickness = pen.Thickness; + shape.StrokeDashArray = dashArray; + shape.StrokeDashOffset = pen.DashOffset; + shape.StrokeStartLineCap = pen.StartLineCap; + shape.StrokeEndLineCap = pen.EndLineCap; + shape.StrokeDashCap = pen.DashCap; + shape.StrokeLineJoin = pen.LineJoin; + shape.StrokeMiterLimit = pen.MiterLimit; + } + } + + //public void DrawRoundedRectangle(Brush brush, Pen pen, Rect rectangle, AnimationClock rectangleAnimations, double radiusX, AnimationClock radiusXAnimations, double radiusY, AnimationClock radiusYAnimations); + //public void DrawText(FormattedText formattedText, Point origin); + //public void DrawVideo(MediaPlayer player, Rect rectangle); + //public void DrawVideo(MediaPlayer player, Rect rectangle, AnimationClock rectangleAnimations); + + public void Pop(int count) + { + Debug.Assert(_canvasStack.Count - 1 > count); + for (int idx = 0; idx < count; idx++) + _canvasStack.Pop(); + } + + public void PushClip(Geometry clipGeometry) + { + Canvas canvas = ActiveCanvas; + if (canvas.Children.Count > 0 || canvas.Clip != null) + canvas = PushCanvas(); + canvas.Clip = clipGeometry; + } + + //public void PushEffect(BitmapEffect effect, BitmapEffectInput effectInput); + //public void PushGuidelineSet(GuidelineSet guidelines); + + public void PushOpacity(double opacity) + { + Canvas canvas = ActiveCanvas; + if (canvas.Children.Count > 0 || !DoubleUtil.IsNaN(canvas.Opacity)) + canvas = PushCanvas(); + canvas.Opacity = opacity; + } + + //public void PushOpacity(double opacity, AnimationClock opacityAnimations); + + public void PushOpacityMask(Brush opacityMask) + { + Canvas canvas = ActiveCanvas; + if (canvas.Children.Count > 0 || canvas.OpacityMask != null) + canvas = PushCanvas(); + canvas.OpacityMask = opacityMask; + } + + public void PushTransform(MatrixTransform transform) + { + Canvas canvas = ActiveCanvas; + if (canvas.Children.Count > 0 || canvas.RenderTransform != null) + canvas = PushCanvas(); + canvas.RenderTransform = transform; + } + + /// + /// Resembles the DrawString function of GDI+. + /// + [Obsolete("Text may be drawn at the wrong position. Requires update!")] + public void DrawString(XGraphics gfx, string text, XFont font, XBrush brush, XRect layoutRectangle, XStringFormat format) + { + double x = layoutRectangle.X; + double y = layoutRectangle.Y; + + double lineSpace = font.GetHeight(); //old: font.GetHeight(gfx); + double cyAscent = lineSpace * font.CellAscent / font.CellSpace; + double cyDescent = lineSpace * font.CellDescent / font.CellSpace; + + bool bold = (font.Style & XFontStyle.Bold) != 0; + bool italic = (font.Style & XFontStyle.Italic) != 0; + bool strikeout = (font.Style & XFontStyle.Strikeout) != 0; + bool underline = (font.Style & XFontStyle.Underline) != 0; + + //Debug.Assert(font.GlyphTypeface != null); + TextBlock textBlock = new TextBlock(); //FontHelper.CreateTextBlock(text, font.GlyphTypeface, font.Size, brush.RealizeWpfBrush()); + if (layoutRectangle.Width > 0) + textBlock.Width = layoutRectangle.Width; + + switch (format.Alignment) + { + case XStringAlignment.Near: + textBlock.TextAlignment = TextAlignment.Left; + break; + + case XStringAlignment.Center: + textBlock.TextAlignment = TextAlignment.Center; + break; + + case XStringAlignment.Far: + textBlock.TextAlignment = TextAlignment.Right; + break; + } + + if (gfx.PageDirection == XPageDirection.Downwards) + { + switch (format.LineAlignment) + { + case XLineAlignment.Near: + //y += cyAscent; + break; + + case XLineAlignment.Center: + // TODO use CapHeight. PDFlib also uses 3/4 of ascent + y += (layoutRectangle.Height - textBlock.ActualHeight) / 2; + //y += -formattedText.Baseline + (font.Size * font.Metrics.CapHeight / font.unitsPerEm / 2) + layoutRectangle.Height / 2; + break; + + case XLineAlignment.Far: + y += layoutRectangle.Height - textBlock.ActualHeight; + //y -= textBlock.ActualHeight; //-formattedText.Baseline - cyDescent + layoutRectangle.Height; + break; + + case XLineAlignment.BaseLine: +//#if !WINDOWS_PHONE + y -= textBlock.BaselineOffset; +//#else +// // No BaselineOffset in Silverlight WP yet. +// //y -= textBlock.BaselineOffset; +//#endif + break; + } + } + else + { + throw new NotImplementedException("XPageDirection.Downwards"); + } + + //if (bold && !descriptor.IsBoldFace) + //{ + // // TODO: emulate bold by thicker outline + //} + + //if (italic && !descriptor.IsBoldFace) + //{ + // // TODO: emulate italic by shearing transformation + //} + + if (underline) + textBlock.TextDecorations = TextDecorations.Underline; + + // No strikethrough in Silverlight + //if (strikeout) + //{ + // formattedText.SetTextDecorations(TextDecorations.Strikethrough); + // //double strikeoutPosition = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutPosition / font.cellSpace; + // //double strikeoutSize = lineSpace * realizedFont.FontDescriptor.descriptor.StrikeoutSize / font.cellSpace; + // //DrawRectangle(null, brush, x, y - strikeoutPosition - strikeoutSize, width, strikeoutSize); + //} + + Canvas.SetLeft(textBlock, x); + Canvas.SetTop(textBlock, y); + ActiveCanvas.Children.Add(textBlock); + } + + ///// + ///// Resembles the MeasureString function of GDI+. + ///// + //[Obsolete("Use XGraphics.MeasureString()")] + //public XSize MeasureString(XGraphics gfx, string text, XFont font, XStringFormat stringFormat) + //{ + // //Debug.Assert(font.GlyphTypeface != null); + // TextBlock textBlock = new TextBlock(); //FontHelper.CreateTextBlock(text, font.GlyphTypeface, font.Size, null); + // // Looks very much like a hack, but is the recommended way documented by Microsoft. + // return new XSize(textBlock.ActualWidth, textBlock.ActualHeight); + //} + + /// + /// Create new canvas and add it to the children of the current canvas. + /// + internal Canvas PushCanvas() + { + Canvas canvas = new Canvas(); + ActiveCanvas.Children.Add(canvas); + _canvasStack.Push(canvas); + return canvas; + } + + /// + /// Gets the currently active canvas. + /// + Canvas ActiveCanvas + { + get { return _canvasStack.Peek(); } + } + + /// + /// Gets the nesting level of Canvas objects. + /// + internal int Level + { + get { return _canvasStack.Count; } + } + readonly Stack _canvasStack = new Stack(); + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgExtensions.cs b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgExtensions.cs new file mode 100644 index 00000000..32e4bd1b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgExtensions.cs @@ -0,0 +1,55 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if SILVERLIGHT +using System.Windows.Media; +using System.Windows.Shapes; + +namespace PdfSharp +{ + static class AgExtensions + { + public static Pen PenFromShape(Shape shape) + { + Pen pen = new Pen(); + pen.Brush = shape.Stroke; + pen.Thickness = shape.StrokeThickness; + // Todo + pen.DashArray = new DoubleCollection(); + pen.StartLineCap = PenLineCap.Flat; + pen.EndLineCap = PenLineCap.Flat; + pen.DashCap = PenLineCap.Flat; + pen.LineJoin = PenLineJoin.Miter; // TODO: check default values + pen.MiterLimit = 10; // TODO: check default values + + return pen; + } + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgHacks.cs b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgHacks.cs new file mode 100644 index 00000000..95fd0d08 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SilverlightInternals/AgHacks.cs @@ -0,0 +1,193 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if SILVERLIGHT || UWP +using System; + +#if SILVERLIGHT +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; +#endif + +/// +/// SerializableAttribute for compatibility with Silverlight. +/// +public class SerializableAttribute : Attribute +{ } + +/// +/// ICloneable for compatibility with Silverlight. +/// +public interface ICloneable +{ + /// + /// Creates a new object that is a copy of the current instance + /// + Object Clone(); +} + +namespace PdfSharp +{ + /// + /// The exception that is thrown when a non-fatal application error occurs. + /// + public class ApplicationException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public ApplicationException() + { } + + /// + /// Initializes a new instance of the class. + /// + public ApplicationException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public ApplicationException(string message, Exception innerException) + : base(message, innerException) + { } + } + +#if SILVERLIGHT + /// + /// The exception that is thrown when the value of an argument is outside + /// the allowable range of values as defined by the invoked method. + /// + public class ArgumentOutOfRangeException : ArgumentException + { + /// + /// Initializes a new instance of the class. + /// + public ArgumentOutOfRangeException() + { } + + /// + /// Initializes a new instance of the class. + /// + public ArgumentOutOfRangeException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + public ArgumentOutOfRangeException(string message, string message2) + : base(message, message2) + { } + + /// + /// Initializes a new instance of the class. + /// + public ArgumentOutOfRangeException(string message, object value, string message2) + : base(message, message2) + { } + + /// + /// Initializes a new instance of the class. + /// + public ArgumentOutOfRangeException(string message, Exception innerException) + : base(message, innerException) + { } + } +#endif + + /// + /// The exception thrown when using invalid arguments that are enumerators. + /// + public class InvalidEnumArgumentException : ArgumentException + { + /// + /// Initializes a new instance of the class. + /// + public InvalidEnumArgumentException() + { } + + /// + /// Initializes a new instance of the class. + /// + public InvalidEnumArgumentException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + public InvalidEnumArgumentException(string message, string message2) + : base(message, message2) + { } + + /// + /// Initializes a new instance of the class. + /// + public InvalidEnumArgumentException(string message, int n, Type type) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + public InvalidEnumArgumentException(string message, Exception innerException) + : base(message, innerException) + { } + } + + //public class FileNotFoundException : Exception + //{ + // public FileNotFoundException() + // { } + + // public FileNotFoundException(string message) + // : base(message) + // { } + + // public FileNotFoundException(string message, string path) + // : base(message + "/" + path) + // { } + + // public FileNotFoundException(string message, Exception innerException) + // : base(message, innerException) + // { } + //} +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/SilverlightInternals/Pen.cs b/src/PDFsharp/src/PdfSharp/SilverlightInternals/Pen.cs new file mode 100644 index 00000000..d3bcf86b --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SilverlightInternals/Pen.cs @@ -0,0 +1,100 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if SILVERLIGHT +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace System.Windows.Media +{ + /// + /// Imitates the WPF Pen object. + /// + internal sealed class Pen + { + public Pen() + { + Thickness = 1; + DashArray = new DoubleCollection(); + StartLineCap = PenLineCap.Flat; + EndLineCap = PenLineCap.Flat; + DashCap = PenLineCap.Flat; + LineJoin = PenLineJoin.Miter; // TODO: check default values + MiterLimit = 10; // TODO: check default values + } + + //public static Pen FromShape(Shape shape) + //{ + // Pen pen = new Pen(); + // pen.Brush = shape.Stroke; + // pen.Thickness = shape.StrokeThickness; + // // Todo + // pen.DashArray = new DoubleCollection(); + // pen.StartLineCap = PenLineCap.Flat; + // pen.EndLineCap = PenLineCap.Flat; + // pen.DashCap = PenLineCap.Flat; + // pen.LineJoin = PenLineJoin.Miter; // TODO: check default values + // pen.MiterLimit = 10; // TODO: check default values + + // return pen; + //} + + public Brush Brush { get; set; } + + public double Thickness { get; set; } + + //public DashStyle DashStyle { get; set; } + + public DoubleCollection DashArray { get; set; } + + public double DashOffset { get; set; } + + public PenLineCap StartLineCap { get; set; } + + public PenLineCap EndLineCap { get; set; } + + public PenLineCap DashCap { get; set; } + + public PenLineJoin LineJoin { get; set; } + + public double MiterLimit { get; set; } + + //public Pen Clone(); + //public Pen CloneCurrentValue(); + } +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/SilverlightInternals/WpHacks.cs b/src/PDFsharp/src/PdfSharp/SilverlightInternals/WpHacks.cs new file mode 100644 index 00000000..44c00404 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/SilverlightInternals/WpHacks.cs @@ -0,0 +1,73 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +#if WINDOWS_PHONE +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +#pragma warning disable 436, 1591 + +namespace System.ComponentModel +{ + //[AttributeUsage(AttributeTargets.All)] + //public sealed class BrowsableAttribute : Attribute + //{ + // public static readonly BrowsableAttribute Yes; + // public static readonly BrowsableAttribute No; + // public static readonly BrowsableAttribute Default; + // public BrowsableAttribute(bool browsable) { } + //} +} + +namespace System.Windows.Media +{ + //public class Typeface + //{ + // public bool TryGetGlyphTypeface(out GlyphTypeface glyphTypeface) + // { + // glyphTypeface = null; + // return false; + // } + //} + + //public sealed class GlyphTypeface + //{ + // public double Version { get; private set; } + // public string FontFileName { get; private set; } + //} +} +#endif diff --git a/src/PDFsharp/src/PdfSharp/StrongnameKey.snk b/src/PDFsharp/src/PdfSharp/StrongnameKey.snk new file mode 100644 index 00000000..ce2edba0 Binary files /dev/null and b/src/PDFsharp/src/PdfSharp/StrongnameKey.snk differ diff --git a/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml new file mode 100644 index 00000000..9a6bc938 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml.cs b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml.cs new file mode 100644 index 00000000..06c32fec --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-ag.xaml.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PdfSharp.Windows +{ + /// + /// A simple page viewer for WPF and Silverlight. + /// Not based on empira application framework. + /// + public partial class PagePreview : UserControl + { + /// + /// Initializes a new instance of the class. + /// + public PagePreview() + { + InitializeComponent(); + + //TextBlock tb = new TextBlock(); + //Binding b = new Binding("FontSize"); + //b.Source = this; + //tb.SetBinding(TextBlock.FontSizeProperty, b); + + //canvasGrid.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvasGrid.SetBinding(HeightProperty, new Binding("CanvasHeight")); + //canvas.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvas.SetBinding(HeightProperty, new Binding("CanvasHeight")); + + LayoutRoot.DataContext = this; + Zoom = (Zoom)100; + } + + void Test() + { + double factor = 1; + int zoom = (int)Zoom; + if (zoom > 0) + factor = Math.Max(Math.Min(zoom, 800), 10) / 100.0; + else + factor = 1; + + canvasGrid.Width = 480 * factor; + canvasGrid.Height = 640 * factor; + scaleTransform.ScaleX = factor; + scaleTransform.ScaleY = factor; + + //CanvasWidth = 480 * factor; + //CanvasHeight = 640 * factor; + //CanvasScaleX = 1 * factor; + //CanvasScaleY = 1 * factor; + } + + /// + /// Gets the canvas. + /// + public Canvas Canvas + { + get { return canvas; } + } + + /// + /// Gets or sets the size of the page 1/96 inch. + /// + public Size PageSize + { + get { return (Size)GetValue(PageSizeProperty); } + set { SetValue(PageSizeProperty, value); } + } + + /// + /// DependencyProperty of PageSize. + /// + public readonly DependencyProperty PageSizeProperty = + DependencyProperty.Register("PageSize", typeof(Size), typeof(PagePreview), new PropertyMetadata(new Size(480, 640), PageSizeChanged)); + + private static void PageSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + + /// + /// Gets or sets the zoom. + /// + public Zoom Zoom + { + get { return (Zoom)GetValue(ZoomProperty); } + set { SetValue(ZoomProperty, value); } + } + + /// + /// DependencyProperty of Zoom. + /// + public readonly DependencyProperty ZoomProperty = + DependencyProperty.Register("Zoom", typeof(Zoom), typeof(PagePreview), new PropertyMetadata(Zoom.FullPage, ZoomChanged)); + + private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + +#if _ + public double CanvasWidth + { + get { return (double)GetValue(CanvasWidthProperty); } + set { SetValue(CanvasWidthProperty, value); } + } + public readonly DependencyProperty CanvasWidthProperty = + DependencyProperty.Register("CanvasWidth", typeof(double), typeof(PagePreview), new PropertyMetadata(210.0)); + + public double CanvasHeight + { + get { return (double)GetValue(CanvasHeightProperty); } + set { SetValue(CanvasHeightProperty, value); } + } + public readonly DependencyProperty CanvasHeightProperty = + DependencyProperty.Register("CanvasHeight", typeof(double), typeof(PagePreview), new PropertyMetadata(297.0)); + + public double CanvasScaleX + { + get { return (double)GetValue(CanvasScaleXProperty); } + set { SetValue(CanvasScaleXProperty, value); } + } + public readonly DependencyProperty CanvasScaleXProperty = + DependencyProperty.Register("CanvasScaleX", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); + + public double CanvasScaleY + { + get { return (double)GetValue(CanvasScaleYProperty); } + set { SetValue(CanvasScaleYProperty, value); } + } + public readonly DependencyProperty CanvasScaleYProperty = + DependencyProperty.Register("CanvasScaleY", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); +#endif + + /// + /// Gets or sets the page visibility. + /// + public Visibility PageVisibility + { + get { return (Visibility)GetValue(PageVisibilityProperty); } + set { SetValue(PageVisibilityProperty, value); } + } + + /// + /// DependencyProperty of PageVisibility. + /// + public readonly DependencyProperty PageVisibilityProperty = + DependencyProperty.Register("PageVisibility", typeof(Visibility), typeof(PagePreview), null); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml new file mode 100644 index 00000000..9a6bc938 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml.cs b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml.cs new file mode 100644 index 00000000..06c32fec --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/PagePreview-wpf.xaml.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PdfSharp.Windows +{ + /// + /// A simple page viewer for WPF and Silverlight. + /// Not based on empira application framework. + /// + public partial class PagePreview : UserControl + { + /// + /// Initializes a new instance of the class. + /// + public PagePreview() + { + InitializeComponent(); + + //TextBlock tb = new TextBlock(); + //Binding b = new Binding("FontSize"); + //b.Source = this; + //tb.SetBinding(TextBlock.FontSizeProperty, b); + + //canvasGrid.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvasGrid.SetBinding(HeightProperty, new Binding("CanvasHeight")); + //canvas.SetBinding(WidthProperty, new Binding("CanvasWidth")); + //canvas.SetBinding(HeightProperty, new Binding("CanvasHeight")); + + LayoutRoot.DataContext = this; + Zoom = (Zoom)100; + } + + void Test() + { + double factor = 1; + int zoom = (int)Zoom; + if (zoom > 0) + factor = Math.Max(Math.Min(zoom, 800), 10) / 100.0; + else + factor = 1; + + canvasGrid.Width = 480 * factor; + canvasGrid.Height = 640 * factor; + scaleTransform.ScaleX = factor; + scaleTransform.ScaleY = factor; + + //CanvasWidth = 480 * factor; + //CanvasHeight = 640 * factor; + //CanvasScaleX = 1 * factor; + //CanvasScaleY = 1 * factor; + } + + /// + /// Gets the canvas. + /// + public Canvas Canvas + { + get { return canvas; } + } + + /// + /// Gets or sets the size of the page 1/96 inch. + /// + public Size PageSize + { + get { return (Size)GetValue(PageSizeProperty); } + set { SetValue(PageSizeProperty, value); } + } + + /// + /// DependencyProperty of PageSize. + /// + public readonly DependencyProperty PageSizeProperty = + DependencyProperty.Register("PageSize", typeof(Size), typeof(PagePreview), new PropertyMetadata(new Size(480, 640), PageSizeChanged)); + + private static void PageSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + + /// + /// Gets or sets the zoom. + /// + public Zoom Zoom + { + get { return (Zoom)GetValue(ZoomProperty); } + set { SetValue(ZoomProperty, value); } + } + + /// + /// DependencyProperty of Zoom. + /// + public readonly DependencyProperty ZoomProperty = + DependencyProperty.Register("Zoom", typeof(Zoom), typeof(PagePreview), new PropertyMetadata(Zoom.FullPage, ZoomChanged)); + + private static void ZoomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + ((PagePreview)d).Test(); + } + +#if _ + public double CanvasWidth + { + get { return (double)GetValue(CanvasWidthProperty); } + set { SetValue(CanvasWidthProperty, value); } + } + public readonly DependencyProperty CanvasWidthProperty = + DependencyProperty.Register("CanvasWidth", typeof(double), typeof(PagePreview), new PropertyMetadata(210.0)); + + public double CanvasHeight + { + get { return (double)GetValue(CanvasHeightProperty); } + set { SetValue(CanvasHeightProperty, value); } + } + public readonly DependencyProperty CanvasHeightProperty = + DependencyProperty.Register("CanvasHeight", typeof(double), typeof(PagePreview), new PropertyMetadata(297.0)); + + public double CanvasScaleX + { + get { return (double)GetValue(CanvasScaleXProperty); } + set { SetValue(CanvasScaleXProperty, value); } + } + public readonly DependencyProperty CanvasScaleXProperty = + DependencyProperty.Register("CanvasScaleX", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); + + public double CanvasScaleY + { + get { return (double)GetValue(CanvasScaleYProperty); } + set { SetValue(CanvasScaleYProperty, value); } + } + public readonly DependencyProperty CanvasScaleYProperty = + DependencyProperty.Register("CanvasScaleY", typeof(double), typeof(PagePreview), new PropertyMetadata(1.0)); +#endif + + /// + /// Gets or sets the page visibility. + /// + public Visibility PageVisibility + { + get { return (Visibility)GetValue(PageVisibilityProperty); } + set { SetValue(PageVisibilityProperty, value); } + } + + /// + /// DependencyProperty of PageVisibility. + /// + public readonly DependencyProperty PageVisibilityProperty = + DependencyProperty.Register("PageVisibility", typeof(Visibility), typeof(PagePreview), null); + } +} diff --git a/src/PDFsharp/src/PdfSharp/Windows/PagePreviewDesignTimeData.cs b/src/PDFsharp/src/PdfSharp/Windows/PagePreviewDesignTimeData.cs new file mode 100644 index 00000000..2d69049e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/PagePreviewDesignTimeData.cs @@ -0,0 +1,28 @@ +using System; +using System.Net; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Ink; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using System.Windows.Shapes; + +namespace PdfSharp.Windows +{ + public class PagePreviewDesignTimeData + { + public double CanvasWidth + { + get { return 210; } + set { } + } + + public double CanvasHeight + { + get { return 297; } + set { } + } + } +} diff --git a/src/PDFsharp/src/PdfSharp/Windows/VisualPresenter.cs b/src/PDFsharp/src/PdfSharp/Windows/VisualPresenter.cs new file mode 100644 index 00000000..44e79b36 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/VisualPresenter.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows; +using System.Windows.Media; + +namespace PdfSharp.Windows +{ + // Create a host visual derived from the FrameworkElement class.// This class provides layout, event handling, and container support for// the child visual objects. + /// + /// Used to present Visuals in the PagePreview. + /// + public class VisualPresenter : FrameworkElement + { + /// + /// Initializes a new instance of the class. + /// + public VisualPresenter() + { + _children = new VisualCollection(this); + } + + /// + /// Gets the number of visual child elements within this element. + /// + protected override int VisualChildrenCount + { + get { return _children.Count; } + } + + /// + /// Overrides , and returns a child at the specified index from a collection of child elements. + /// + protected override Visual GetVisualChild(int index) + { + if (index < 0 || index >= _children.Count) + throw new ArgumentOutOfRangeException("index"); + + return _children[index]; + } + + /// + /// Gets the children collection. + /// + public VisualCollection Children + { + get { return _children; } + } + readonly VisualCollection _children; + } +} diff --git a/src/PDFsharp/src/PdfSharp/Windows/enums/RenderMode.cs b/src/PDFsharp/src/PdfSharp/Windows/enums/RenderMode.cs new file mode 100644 index 00000000..95c3a8f2 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/enums/RenderMode.cs @@ -0,0 +1,52 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Windows +{ + /// + /// Specifies how to render the preview. + /// + public enum RenderMode + { + /// + /// Draw retained + /// + Default = 0, + + ///// + ///// Draw using a metafile + ///// + //Metafile = 1, + + ///// + ///// Draw using a bitmap image. + ///// + //Bitmap = 2 + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/Windows/enums/Zoom.cs b/src/PDFsharp/src/PdfSharp/Windows/enums/Zoom.cs new file mode 100644 index 00000000..284b9642 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/Windows/enums/Zoom.cs @@ -0,0 +1,118 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp.Windows +{ + /// + /// Defines a zoom factor used in the preview control. + /// + public enum Zoom + { + /// + /// The smallest possible zoom factor. + /// + Mininum = 10, + + /// + /// The largest possible zoom factor. + /// + Maximum = 800, + + /// + /// A pre-defined zoom factor. + /// + Percent800 = 800, + + /// + /// A pre-defined zoom factor. + /// + Percent600 = 600, + + /// + /// A pre-defined zoom factor. + /// + Percent400 = 400, + + /// + /// A pre-defined zoom factor. + /// + Percent200 = 200, + + /// + /// A pre-defined zoom factor. + /// + Percent150 = 150, + + /// + /// A pre-defined zoom factor. + /// + Percent100 = 100, + + /// + /// A pre-defined zoom factor. + /// + Percent75 = 75, + + /// + /// A pre-defined zoom factor. + /// + Percent50 = 50, + + /// + /// A pre-defined zoom factor. + /// + Percent25 = 25, + + /// + /// A pre-defined zoom factor. + /// + Percent10 = 10, + + /// + /// Sets the percent value such that the document fits horizontally into the window. + /// + BestFit = -1, + + /// + /// Sets the percent value such that the printable area of the document fits horizontally into the window. + /// Currently not yet implemented and the same as ZoomBestFit. + /// + TextFit = -2, + + /// + /// Sets the percent value such that the whole document fits completely into the window. + /// + FullPage = -3, + + /// + /// Sets the percent value such that the document is displayed in its real physical size. + /// + OriginalSize = -4, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/root/PSSR.cs b/src/PDFsharp/src/PdfSharp/root/PSSR.cs new file mode 100644 index 00000000..232a5a84 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/PSSR.cs @@ -0,0 +1,400 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Diagnostics; +using System.Resources; +using System.Reflection; +using PdfSharp.Drawing; +using PdfSharp.Internal; +using PdfSharp.Pdf; + +#pragma warning disable 1591 + +namespace PdfSharp +{ + /// + /// The Pdf-Sharp-String-Resources. + /// + // ReSharper disable once InconsistentNaming + static class PSSR + { + // How to use: + // Create a function or property for each message text, depending on how many parameters are + // part of the message. For the beginning, type plain English text in the function or property. + // The use of functions is safe when a parameter must be changed. The compiler tells you all + // places in your code that must be modified. + // For localization, create an enum value for each function or property with the same name. Then + // create localized message files with the enum values as messages identifiers. In the properties + // and functions all text is replaced by Format or GetString functions with the corresponding enum value + // as first parameter. The use of enums ensures that typing errors in message resource names are + // simply impossible. Use the TestResourceMessages function to ensure that each enum value has an + // appropriate message text. + + #region Helper functions + /// + /// Loads the message from the resource associated with the enum type and formats it + /// using 'String.Format'. Because this function is intended to be used during error + /// handling it never raises an exception. + /// + /// The type of the parameter identifies the resource + /// and the name of the enum identifies the message in the resource. + /// Parameters passed through 'String.Format'. + /// The formatted message. + public static string Format(PSMsgID id, params object[] args) + { + string message; + try + { + message = GetString(id); + message = message != null ? Format(message, args) : "INTERNAL ERROR: Message not found in resources."; + return message; + } + catch (Exception ex) + { + message = String.Format("UNEXPECTED ERROR while formatting message with ID {0}: {1}", id.ToString(), ex.ToString()); + } + return message; + } + + public static string Format(string format, params object[] args) + { + if (format == null) + throw new ArgumentNullException("format"); + + string message; + try + { + message = String.Format(format, args); + } + catch (Exception ex) + { + message = String.Format("UNEXPECTED ERROR while formatting message '{0}': {1}", format, ex); + } + return message; + } + + /// + /// Gets the localized message identified by the specified DomMsgID. + /// + public static string GetString(PSMsgID id) + { + return ResMngr.GetString(id.ToString()); + } + + #endregion + + #region General messages + + public static string IndexOutOfRange + { + get { return "The index is out of range."; } + } + + public static string ListEnumCurrentOutOfRange + { + get { return "Enumeration out of range."; } + } + + public static string PageIndexOutOfRange + { + get { return "The index of a page is out of range."; } + } + + public static string OutlineIndexOutOfRange + { + get { return "The index of an outline is out of range."; } + } + + public static string SetValueMustNotBeNull + { + get { return "The set value property must not be null."; } + } + + public static string InvalidValue(int val, string name, int min, int max) + { + return Format("{0} is not a valid value for {1}. {1} should be greater than or equal to {2} and less than or equal to {3}.", + val, name, min, max); + } + + public static string ObsoleteFunktionCalled + { + get { return "The function is obsolete and must not be called."; } + } + + public static string OwningDocumentRequired + { + get { return "The PDF object must belong to a PdfDocument, but property Document is null."; } + } + + public static string FileNotFound(string path) + { + return Format("The file '{0}' does not exist.", path); + } + + public static string FontDataReadOnly + { + get { return "Font data is read-only."; } + } + + public static string ErrorReadingFontData + { + get { return "Error while parsing an OpenType font."; } + } + + #endregion + + #region XGraphics specific messages + + // ----- XGraphics ---------------------------------------------------------------------------- + + public static string PointArrayEmpty + { + get { return "The PointF array must not be empty."; } + } + + public static string PointArrayAtLeast(int count) + { + return Format("The point array must contain {0} or more points.", count); + } + + public static string NeedPenOrBrush + { + get { return "XPen or XBrush or both must not be null."; } + } + + public static string CannotChangeImmutableObject(string typename) + { + return String.Format("You cannot change this immutable {0} object.", typename); + } + + public static string FontAlreadyAdded(string fontname) + { + return String.Format("Fontface with the name '{0}' already added to font collection.", fontname); + } + + public static string NotImplementedForFontsRetrievedWithFontResolver(string name) + { + return String.Format("Not implemented for font '{0}', because it was retrieved with font resolver.", name); + } + + #endregion + + #region PDF specific messages + + // ----- PDF ---------------------------------------------------------------------------------- + + public static string InvalidPdf + { + get { return "The file is not a valid PDF document."; } + } + + public static string InvalidVersionNumber + { + get { return "Invalid version number. Valid values are 12, 13, and 14."; } + } + + public static string CannotHandleXRefStreams + { + get { return "Cannot handle cross-reference streams. The current implementation of PDFsharp cannot handle this PDF feature introduced with Acrobat 6."; } + } + + public static string PasswordRequired + { + get { return "A password is required to open the PDF document."; } + } + + public static string InvalidPassword + { + get { return "The specified password is invalid."; } + } + + public static string OwnerPasswordRequired + { + get { return "To modify the document the owner password is required"; } + } + + public static string UserOrOwnerPasswordRequired + { + get { return GetString(PSMsgID.UserOrOwnerPasswordRequired); } + //get { return "At least a user or an owner password is required to encrypt the document."; } + } + + public static string CannotModify + { + get { return "The document cannot be modified."; } + } + + public static string NameMustStartWithSlash + { + //get { return GetString(PSMsgID.NameMustStartWithSlash); } + get { return "A PDF name must start with a slash (/)."; } + } + + public static string ImportPageNumberOutOfRange(int pageNumber, int maxPage, string path) + { + return String.Format("The page cannot be imported from document '{2}', because the page number is out of range. " + + "The specified page number is {0}, but it must be in the range from 1 to {1}.", pageNumber, maxPage, path); + } + + public static string MultiplePageInsert + { + get { return "The page cannot be added to this document because the document already owned this page."; } + } + + public static string UnexpectedTokenInPdfFile + { + get { return "Unexpected token in PDF file. The PDF file may be corrupt. If it is not, please send us the file for service."; } + } + + public static string InappropriateColorSpace(PdfColorMode colorMode, XColorSpace colorSpace) + { + string mode; + switch (colorMode) + { + case PdfColorMode.Rgb: + mode = "RGB"; + break; + + case PdfColorMode.Cmyk: + mode = "CMYK"; + break; + + default: + mode = "(undefined)"; + break; + } + + string space; + switch (colorSpace) + { + case XColorSpace.Rgb: + space = "RGB"; + break; + + case XColorSpace.Cmyk: + space = "CMYK"; + break; + + case XColorSpace.GrayScale: + space = "grayscale"; + break; + + default: + space = "(undefined)"; + break; + } + return String.Format("The document requires color mode {0}, but a color is defined using {1}. " + + "Use only colors that match the color mode of the PDF document", mode, space); + } + + public static string CannotGetGlyphTypeface(string fontName) + { + return Format("Cannot get a matching glyph typeface for font '{0}'.", fontName); + } + + + // ----- PdfParser ---------------------------------------------------------------------------- + + public static string UnexpectedToken(string token) + { + return Format(PSMsgID.UnexpectedToken, token); + //return Format("Token '{0}' was not expected.", token); + } + + public static string UnknownEncryption + { + get { return GetString(PSMsgID.UnknownEncryption); } + //get { return "The PDF document is protected with an encryption not supported by PDFsharp."; } + } + + #endregion + + #region Resource manager + + /// + /// Gets the resource manager for this module. + /// + public static ResourceManager ResMngr + { + get + { + if (_resmngr == null) + { + try + { + Lock.EnterFontFactory(); + if (_resmngr == null) + { +#if true_ + // Force the English language. + System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.InvariantCulture; +#endif +#if !NETFX_CORE && !UWP + _resmngr = new ResourceManager("PdfSharp.Resources.Messages", + Assembly.GetExecutingAssembly()); +#else + _resmngr = new ResourceManager("PdfSharp.Resources.Messages", + typeof(PSSR).GetTypeInfo().Assembly); +#endif + } + } + finally { Lock.ExitFontFactory(); } + } + return _resmngr; + } + } + static ResourceManager _resmngr; + + /// + /// Writes all messages defined by PSMsgID. + /// + [Conditional("DEBUG")] + public static void TestResourceMessages() + { +#if !SILVERLIGHT + string[] names = Enum.GetNames(typeof(PSMsgID)); + foreach (string name in names) + { + string message = String.Format("{0}: '{1}'", name, ResMngr.GetString(name)); + Debug.Assert(message != null); + Debug.WriteLine(message); + } +#else +#endif + } + + static PSSR() + { + TestResourceMessages(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/root/PageSizeConverter.cs b/src/PDFsharp/src/PdfSharp/root/PageSizeConverter.cs new file mode 100644 index 00000000..df8161ee --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/PageSizeConverter.cs @@ -0,0 +1,181 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if GDI +using System.Drawing; +#endif +#if WPF +using System.Windows; +#endif +using PdfSharp.Drawing; + +namespace PdfSharp +{ + /// + /// Converter from to . + /// + public static class PageSizeConverter + { + /// + /// Converts the specified page size enumeration to a pair of values in point. + /// + public static XSize ToSize(PageSize value) + { + // The international definitions are: + // 1 inch == 25.4 mm + // 1 inch == 72 point + switch (value) + { + // Source http://www.din-formate.de/reihe-a-din-groessen-mm-pixel-dpi.html + case PageSize.A0: + return new XSize(2384, 3370); + + case PageSize.A1: + return new XSize(1684, 2384); + + case PageSize.A2: + return new XSize(1191, 1684); + + case PageSize.A3: + return new XSize(842, 1191); + + case PageSize.A4: + return new XSize(595, 842); + + case PageSize.A5: + return new XSize(420, 595); + + + case PageSize.RA0: + return new XSize(2438, 3458); + + case PageSize.RA1: + return new XSize(1729, 2438); + + case PageSize.RA2: + return new XSize(1219, 1729); + + case PageSize.RA3: + return new XSize(865, 1219); + + case PageSize.RA4: + return new XSize(609, 865); + + case PageSize.RA5: + return new XSize(433, 609); + + + case PageSize.B0: + return new XSize(2835, 4008); + + case PageSize.B1: + return new XSize(2004, 2835); + + case PageSize.B2: + return new XSize(1417, 2004); + + case PageSize.B3: + return new XSize(1001, 1417); + + case PageSize.B4: + return new XSize(709, 1001); + + case PageSize.B5: + return new XSize(499, 709); + + // The non-ISO sizes ... + + case PageSize.Quarto: // 8 x 10 inch + return new XSize(576, 720); + + case PageSize.Foolscap: // 8 x 13 inch + return new XSize(576, 936); + + case PageSize.Executive: // 7.5 x 10 inch + return new XSize(540, 720); + + case PageSize.GovernmentLetter: // 8 x 10.5 inch + return new XSize(576, 756); + + case PageSize.Letter: // 8.5 x 11 inch + return new XSize(612, 792); + + case PageSize.Legal: // 8.5 x 14 inch + return new XSize(612, 1008); + + case PageSize.Ledger: // 17 x 11 inch + return new XSize(1224, 792); + + case PageSize.Tabloid: // 11 x 17 inch + return new XSize(792, 1224); + + case PageSize.Post: // 15.5 x 19.25 inch + return new XSize(1126, 1386); + + case PageSize.Crown: // 20 x 15 inch + return new XSize(1440, 1080); + + case PageSize.LargePost: // 16.5 x 21 inch + return new XSize(1188, 1512); + + case PageSize.Demy: // 17.5 x 22 inch + return new XSize(1260, 1584); + + case PageSize.Medium: // 18 x 23 inch + return new XSize(1296, 1656); + + case PageSize.Royal: // 20 x 25 inch + return new XSize(1440, 1800); + + case PageSize.Elephant: // 23 x 28 inch + return new XSize(1565, 2016); + + case PageSize.DoubleDemy: // 23.5 x 35 inch + return new XSize(1692, 2520); + + case PageSize.QuadDemy: // 35 x 45 inch + return new XSize(2520, 3240); + + case PageSize.STMT: // 5.5 x 8.5 inch + return new XSize(396, 612); + + case PageSize.Folio: // 8.5 x 13 inch + return new XSize(612, 936); + + case PageSize.Statement: // 5.5 x 8.5 inch + return new XSize(396, 612); + + case PageSize.Size10x14: // 10 x 14 inch + return new XSize(720, 1008); + } + throw new ArgumentException("Invalid PageSize.", "value"); + } + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/root/PdfSharpException.cs b/src/PDFsharp/src/PdfSharp/root/PdfSharpException.cs new file mode 100644 index 00000000..3e683579 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/PdfSharpException.cs @@ -0,0 +1,64 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp +{ + /// + /// Base class of all exceptions in the PDFsharp frame work. + /// + public class PdfSharpException : Exception + { + // The class is not yet used + + /// + /// Initializes a new instance of the class. + /// + public PdfSharpException() + { } + + /// + /// Initializes a new instance of the class. + /// + /// The exception message. + public PdfSharpException(string message) + : base(message) + { } + + /// + /// Initializes a new instance of the class. + /// + /// The exception message. + /// The inner exception. + public PdfSharpException(string message, Exception innerException) : + base(message, innerException) + { } + } +} diff --git a/src/PDFsharp/src/PdfSharp/root/ProductVersionInfo.cs b/src/PDFsharp/src/PdfSharp/root/ProductVersionInfo.cs new file mode 100644 index 00000000..cce64cb9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/ProductVersionInfo.cs @@ -0,0 +1,274 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace PdfSharp +{ + /// + /// Version info base for all PDFsharp related assemblies. + /// + public static class ProductVersionInfo + { + /// + /// The title of the product. + /// + public const string Title = "PDFsharp"; + + /// + /// A characteristic description of the product. + /// + public const string Description = "A .NET library for processing PDF."; + + /// + /// The PDF producer information string. + /// TODO: Called Creator in MigraDoc??? + /// + public const string Producer = Title + " " + VersionMajor + "." + VersionMinor + "." + VersionBuild + Technology + " (" + Url + ")"; + + /// + /// The PDF producer information string including VersionPatch. + /// + public const string Producer2 = Title + " " + VersionMajor + "." + VersionMinor + "." + VersionBuild + "." + VersionPatch + Technology + " (" + Url + ")"; + + /// + /// The full version number. + /// + public const string Version = VersionMajor + "." + VersionMinor + "." + VersionBuild + "." + VersionPatch; + + /// + /// The full version string. + /// + public const string Version2 = VersionMajor + "." + VersionMinor + "." + VersionBuild + "." + VersionPatch + Technology; + + /// + /// The home page of this product. + /// + public const string Url = "www.pdfsharp.com"; + + /// + /// Unused. + /// + public const string Configuration = ""; + + /// + /// The company that created/owned the product. + /// + public const string Company = "empira Software GmbH, Cologne Area (Germany)"; + + /// + /// The name the product. + /// + public const string Product = "PDFsharp"; + + /// + /// The copyright information. + /// + public const string Copyright = "Copyright 2005-2019 empira Software GmbH."; + + /// + /// The trademark the product. + /// + public const string Trademark = "PDFsharp"; + + /// + /// Unused. + /// + public const string Culture = ""; + + /// + /// The major version number of the product. + /// + public const string VersionMajor = "1"; + + /// + /// The minor version number of the product. + /// + public const string VersionMinor = "51"; + + /// + /// The build number of the product. + /// + public const string VersionBuild = "5185"; // V16G // Build = days since 2005-01-01 - change this values ONLY HERE + + /// + /// The patch number of the product. + /// + public const string VersionPatch = "0"; + + /// + /// The Version Prerelease String for NuGet. + /// + public const string VersionPrerelease = "beta"; // "" for stable Release, e.g. "beta" or "rc.1.2" for Prerelease. // Also used for NuGet Version. + +#if DEBUG + /// + /// The calculated build number. + /// +// ReSharper disable RedundantNameQualifier + public static int BuildNumber = (System.DateTime.Now - new System.DateTime(2005, 1, 1)).Days; + // ReSharper restore RedundantNameQualifier +#endif + + /// + /// E.g. "2005-01-01", for use in NuGet Script. + /// + public const string VersionReferenceDate = "2005-01-01"; + + /// + /// Use _ instead of blanks and special characters. Can be complemented with a suffix in the NuGet Script. + /// Nuspec Doc: The unique identifier for the package. This is the package name that is shown when packages + /// are listed using the Package Manager Console. These are also used when installing a package using the + /// Install-Package command within the Package Manager Console. Package IDs may not contain any spaces + /// or characters that are invalid in an URL. In general, they follow the same rules as .NET namespaces do. + /// So Foo.Bar is a valid ID, Foo! and Foo Bar are not. + /// + public const string NuGetID = "PDFsharp"; + + /// + /// Nuspec Doc: The human-friendly title of the package displayed in the Manage NuGet Packages dialog. + /// If none is specified, the ID is used instead. + /// + public const string NuGetTitle = "PDFsharp"; + + /// + /// Nuspec Doc: A comma-separated list of authors of the package code. + /// + public const string NuGetAuthors = "empira Software GmbH"; + + /// + /// Nuspec Doc: A comma-separated list of the package creators. This is often the same list as in authors. + /// This is ignored when uploading the package to the NuGet.org Gallery. + /// + public const string NuGetOwners = "empira Software GmbH"; + + /// + /// Nuspec Doc: A long description of the package. This shows up in the right pane of the Add Package Dialog + /// as well as in the Package Manager Console when listing packages using the Get-Package command. + /// + // This assignment must be written in one line because it will be parsed from a PS1 file. + public const string NuGetDescription = "PDFsharp is the Open Source .NET library that easily creates and processes PDF documents on the fly from any .NET language. The same drawing routines can be used to create PDF documents, draw on the screen, or send output to any printer."; + + /// + /// Nuspec Doc: A description of the changes made in each release of the package. This field only shows up + /// when the _Updates_ tab is selected and the package is an update to a previously installed package. + /// It is displayed where the Description would normally be displayed. + /// + public const string NuGetReleaseNotes = ""; + + /// + /// Nuspec Doc: A short description of the package. If specified, this shows up in the middle pane of the + /// Add Package Dialog. If not specified, a truncated version of the description is used instead. + /// + public const string NuGetSummary = "A .NET library for processing PDF."; + + /// + /// Nuspec Doc: The locale ID for the package, such as en-us. + /// + public const string NuGetLanguage = ""; + + /// + /// Nuspec Doc: A URL for the home page of the package. + /// + /// + /// http://www.pdfsharp.net/NuGetPackage_PDFsharp-GDI.ashx + /// http://www.pdfsharp.net/NuGetPackage_PDFsharp-WPF.ashx + /// + public const string NuGetProjectUrl = "www.pdfsharp.net"; + + /// + /// Nuspec Doc: A URL for the image to use as the icon for the package in the Manage NuGet Packages + /// dialog box. This should be a 32x32-pixel .png file that has a transparent background. + /// + public const string NuGetIconUrl = "http://www.pdfsharp.net/resources/PDFsharp-Logo-32x32.png"; + + /// + /// Nuspec Doc: A link to the license that the package is under. + /// + public const string NuGetLicenseUrl = "http://www.pdfsharp.net/PDFsharp_License.ashx"; + + /// + /// Nuspec Doc: A Boolean value that specifies whether the client needs to ensure that the package license (described by licenseUrl) is accepted before the package is installed. + /// + public const bool NuGetRequireLicenseAcceptance = false; + + /// + /// Nuspec Doc: A space-delimited list of tags and keywords that describe the package. This information is used to help make sure users can find the package using + /// searches in the Add Package Reference dialog box or filtering in the Package Manager Console window. + /// + public const string NuGetTags = "PDFsharp PDF creation"; + + /// + /// The technology tag of the product: + /// (none) Pure .NET + /// -gdi : GDI+, + /// -wpf : WPF, + /// -hybrid : Both GDI+ and WPF (hybrid). + /// -sl : Silverlight + /// -wp : Windows Phone + /// -wrt : Windows RunTime + /// +#if GDI && !WPF + // GDI+ (System.Drawing) + public const string Technology = "-gdi"; +#endif +#if WPF && !GDI && !SILVERLIGHT + // Windows Presentation Foundation + public const string Technology = "-wpf"; +#endif +#if WPF && GDI + // Hybrid - for testing only + public const string Technology = "-h"; +#endif +#if SILVERLIGHT && !WINDOWS_PHONE + // Silverlight 5 + public const string Technology = "-sl"; +#endif +#if WINDOWS_PHONE + // Windows Phone + public const string Technology = "-wp"; +#endif +#if NETFX_CORE + // WinRT + public const string Technology = "-wrt"; +#endif +#if UWP + // Windows Universal App + public const string Technology = "-uwp"; +#endif +#if DNC10 + // .net Core + public const string Technology = "-dnc"; +#endif +#if CORE + // .net classic without GDI+ and WPF + public const string Technology = ""; // no extension +#endif + } +} diff --git a/src/PDFsharp/src/PdfSharp/root/VersionInfo.cs b/src/PDFsharp/src/PdfSharp/root/VersionInfo.cs new file mode 100644 index 00000000..9fed5912 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/VersionInfo.cs @@ -0,0 +1,49 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp +{ + /// + /// Version info of this assembly. + /// + static class VersionInfo + { + public const string Title = ProductVersionInfo.Title; + public const string Description = ProductVersionInfo.Description; + public const string Producer = ProductVersionInfo.Producer; + public const string Version = ProductVersionInfo.Version; + public const string Url = ProductVersionInfo.Url; + public const string Configuration = ""; + public const string Company = ProductVersionInfo.Company; + public const string Product = ProductVersionInfo.Product; + public const string Copyright = ProductVersionInfo.Copyright; + public const string Trademark = ProductVersionInfo.Trademark; + public const string Culture = ""; + } +} diff --git a/src/PDFsharp/src/PdfSharp/root/enums/PSMsgID.cs b/src/PDFsharp/src/PdfSharp/root/enums/PSMsgID.cs new file mode 100644 index 00000000..375d2e3e --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/enums/PSMsgID.cs @@ -0,0 +1,76 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp +{ + /// + /// Represents IDs for error and diagnostic messages generated by PDFsharp. + /// + // ReSharper disable once InconsistentNaming + enum PSMsgID + { + // ----- General Messages --------------------------------------------------------------------- + + /// + /// PSMsgID. + /// + SampleMessage1, + + /// + /// PSMsgID. + /// + SampleMessage2, + + // ----- XGraphics Messages --------------------------------------------------------------- + + // ----- PDF Messages ---------------------------------------------------------------------- + + /// + /// PSMsgID. + /// + NameMustStartWithSlash, + + /// + /// PSMsgID. + /// + UserOrOwnerPasswordRequired, + + // ----- PdfParser Messages ------------------------------------------------------------------- + + /// + /// PSMsgID. + /// + UnexpectedToken, + + /// + /// PSMsgID. + /// + UnknownEncryption, + } +} \ No newline at end of file diff --git a/src/PDFsharp/src/PdfSharp/root/enums/PageOrientation.cs b/src/PDFsharp/src/PdfSharp/root/enums/PageOrientation.cs new file mode 100644 index 00000000..69fb73bd --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/enums/PageOrientation.cs @@ -0,0 +1,54 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp +{ + /// + /// Base namespace of PDFsharp. Most classes are implemented in nested namespaces like e. g. PdfSharp.Pdf. + /// + /// + [System.Runtime.CompilerServices.CompilerGenerated] + internal class NamespaceDoc { } + + /// + /// Specifies the orientation of a page. + /// + public enum PageOrientation + { + /// + /// The default page orientation. + /// + Portrait, + + /// + /// The width and height of the page are reversed. + /// + Landscape, + } +} diff --git a/src/PDFsharp/src/PdfSharp/root/enums/PageSize.cs b/src/PDFsharp/src/PdfSharp/root/enums/PageSize.cs new file mode 100644 index 00000000..e6b6dfe9 --- /dev/null +++ b/src/PDFsharp/src/PdfSharp/root/enums/PageSize.cs @@ -0,0 +1,281 @@ +#region PDFsharp - A .NET library for processing PDF +// +// Authors: +// Stefan Lange +// +// Copyright (c) 2005-2019 empira Software GmbH, Cologne Area (Germany) +// +// http://www.pdfsharp.com +// http://sourceforge.net/projects/pdfsharp +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#endregion + +namespace PdfSharp +{ + /// + /// Identifies the most popular predefined page sizes. + /// + public enum PageSize + { + /// + /// The width or height of the page are set manually and override the PageSize property. + /// + Undefined = 0, + + // ISO formats (link is dead in the meantime) + // see http://www.engineeringtoolbox.com/drawings-paper-sheets-sizes-25_349.html + + /// + /// Identifies a paper sheet size of 841 mm times 1189 mm or 33.11 inch times 46.81 inch. + /// + A0 = 1, + + /// + /// Identifies a paper sheet size of 594 mm times 841 mm or 23.39 inch times 33.1 inch. + /// + A1 = 2, + + /// + /// Identifies a paper sheet size of 420 mm times 594 mm or 16.54 inch times 23.29 inch. + /// + A2 = 3, + + /// + /// Identifies a paper sheet size of 297 mm times 420 mm or 11.69 inch times 16.54 inch. + /// + A3 = 4, + + /// + /// Identifies a paper sheet size of 210 mm times 297 mm or 8.27 inch times 11.69 inch. + /// + A4 = 5, + + /// + /// Identifies a paper sheet size of 148 mm times 210 mm or 5.83 inch times 8.27 inch. + /// + A5 = 6, + + /// + /// Identifies a paper sheet size of 860 mm times 1220 mm. + /// + RA0 = 7, + + /// + /// Identifies a paper sheet size of 610 mm times 860 mm. + /// + RA1 = 8, + + /// + /// Identifies a paper sheet size of 430 mm times 610 mm. + /// + RA2 = 9, + + /// + /// Identifies a paper sheet size of 305 mm times 430 mm. + /// + RA3 = 10, + + /// + /// Identifies a paper sheet size of 215 mm times 305 mm. + /// + RA4 = 11, + + /// + /// Identifies a paper sheet size of 153 mm times 215 mm. + /// + RA5 = 12, + + /// + /// Identifies a paper sheet size of 1000 mm times 1414 mm or 39.37 inch times 55.67 inch. + /// + B0 = 13, + + /// + /// Identifies a paper sheet size of 707 mm times 1000 mm or 27.83 inch times 39.37 inch. + /// + B1 = 14, + + /// + /// Identifies a paper sheet size of 500 mm times 707 mm or 19.68 inch times 27.83 inch. + /// + B2 = 15, + + /// + /// Identifies a paper sheet size of 353 mm times 500 mm or 13.90 inch times 19.68 inch. + /// + B3 = 16, + + /// + /// Identifies a paper sheet size of 250 mm times 353 mm or 9.84 inch times 13.90 inch. + /// + B4 = 17, + + /// + /// Identifies a paper sheet size of 176 mm times 250 mm or 6.93 inch times 9.84 inch. + /// + B5 = 18, + +#if true_ + /// + /// Identifies a paper sheet size of 917 mm times 1297 mm or 36.00 inch times 51.20 inch. + /// + C0 = 19, + + /// + /// Identifies a paper sheet size of 648 mm times 917 mm or 25.60 inch times 36.00 inch. + /// + C1 = 20, + + /// + /// Identifies a paper sheet size of 458 mm times 648 mm or 18.00 inch times 25.60 inch. + /// + C2 = 21, + + /// + /// Identifies a paper sheet size of 324 mm times 458 mm or 12.80 inch times 18.00 inch. + /// + C3 = 22, + + /// + /// Identifies a paper sheet size of 229 mm times 324 mm or 9.00 inch times 12.80 inch. + /// + C4 = 23, + + /// + /// Identifies a paper sheet size of 162 mm times 229 mm or 6.40 inch times 9.0 inch. + /// + C5 = 24, +#endif + + // Current U.S. loose paper sizes + // see http://www.reference.com/browse/wiki/Paper_size + + /// + /// Identifies a paper sheet size of 10 inch times 8 inch or 254 mm times 203 mm. + /// + Quarto = 100, + + /// + /// Identifies a paper sheet size of 13 inch times 8 inch or 330 mm times 203 mm. + /// + Foolscap = 101, + + /// + /// Identifies a paper sheet size of 10.5 inch times 7.25 inch or 267 mm times 184 mm. + /// + Executive = 102, + + /// + /// Identifies a paper sheet size of 10.5 inch times 8 inch 267 mm times 203 mm. + /// + GovernmentLetter = 103, + + /// + /// Identifies a paper sheet size of 11 inch times 8.5 inch 279 mm times 216 mm. + /// + Letter = 104, + + /// + /// Identifies a paper sheet size of 14 inch times 8.5 inch 356 mm times 216 mm. + /// + Legal = 105, + + /// + /// Identifies a paper sheet size of 17 inch times 11 inch or 432 mm times 279 mm. + /// + Ledger = 106, + + /// + /// Identifies a paper sheet size of 17 inch times 11 inch or 432 mm times 279 mm. + /// + Tabloid = 107, + + /// + /// Identifies a paper sheet size of 19.25 inch times 15.5 inch 489 mm times 394 mm. + /// + Post = 108, + + /// + /// 20 Identifies a paper sheet size of 20 inch times 15 inch or 508 mm times 381 mm. + /// + Crown = 109, + + /// + /// Identifies a paper sheet size of 21 inch times 16.5 inch 533 mm times 419 mm. + /// + LargePost = 110, + + /// + /// Identifies a paper sheet size of 22.5 inch times 17.5 inch 572 mm times 445 mm. + /// + Demy = 111, + + /// + /// Identifies a paper sheet size of 23 inch times 18 inch or 584 mm times 457 mm. + /// + Medium = 112, + + /// + /// Identifies a paper sheet size of 25 inch times 20 inch or 635 mm times 508 mm. + /// + Royal = 113, + + /// + /// Identifies a paper sheet size of 28 inch times 23 inch or 711 mm times 584 mm. + /// + Elephant = 114, + + /// + /// Identifies a paper sheet size of 35 inch times 23.5 inch or 889 mm times 597 mm. + /// + DoubleDemy = 115, + + /// + /// Identifies a paper sheet size of 45 inch times 35 inch 1143 times 889 mm. + /// + QuadDemy = 116, + + /// + /// Identifies a paper sheet size of 8.5 inch times 5.5 inch or 216 mm times 396 mm. + /// + STMT = 117, + + /// + /// Identifies a paper sheet size of 8.5 inch times 13 inch or 216 mm times 330 mm. + /// + Folio = 120, + + /// + /// Identifies a paper sheet size of 5.5 inch times 8.5 inch or 396 mm times 216 mm. + /// + Statement = 121, + + /// + /// Identifies a paper sheet size of 10 inch times 14 inch. + /// + Size10x14 = 122, + + //A 11 8.5 279 216 + //B 17 11 432 279 + //C 22 17 559 432 + //D 34 22 864 559 + //E 44 34 1118 864 + } +} \ No newline at end of file diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index 8524182e..af2fa6eb 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -1,108 +1,12 @@ - - - - - - - Debug - AnyCPU - {B3B77F1B-1309-4E44-BE70-E9BAE9F511DB} - Library - Properties - Tests - NClass.Tests - v4.7.2 - 512 - - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\NUnit.3.12.0\lib\net45\nunit.framework.dll - - - ..\packages\Shouldly.3.0.2\lib\net451\Shouldly.dll - - - ..\packages\Shouldly.3.0.2\lib\net451\Shouldly.dll - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - Designer - - - - - {8CF10505-3C2E-4E45-AB90-21613237B412} - Core - - - {755B2714-62DB-419C-9097-49C08439E7E3} - CSharp - - - {f52a7548-e60a-485b-9c84-1d2871416db7} - DiagramEditor - - - {255565B1-3246-4184-9F71-19178FE0BA65} - Java - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - + + + net5.0-windows7.0 + + + + + + + + \ No newline at end of file diff --git a/src/Translations/Properties/AssemblyInfo.cs b/src/Translations/Properties/AssemblyInfo.cs deleted file mode 100644 index 539d1929..00000000 --- a/src/Translations/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NClass.Translations")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NClass.Translations")] -[assembly: AssemblyCopyright("Copyright © Georgi Baychev 2016-2020, Balazs Tihanyi 2006-2009")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("a51ac977-ec99-4279-ab73-eda9418d9f2e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("2.8.0.2")] -[assembly: AssemblyFileVersion("2.8.0.0")] diff --git a/src/Translations/Translations.build b/src/Translations/Translations.build deleted file mode 100644 index 2073002b..00000000 --- a/src/Translations/Translations.build +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/Translations/Translations.csproj b/src/Translations/Translations.csproj index 2ba6f7f2..046de52d 100644 --- a/src/Translations/Translations.csproj +++ b/src/Translations/Translations.csproj @@ -1,141 +1,6 @@ - - + - Debug - AnyCPU - 9.0.30729 - 2.0 - {B3B7D798-3D52-47F0-B1A7-A91BC5FE184F} - Library - Properties - NClass.Translations + net5.0-windows7.0 NClass.Translations - - - 3.5 - - - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - v4.7.2 - - - - - v4.5 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AllRules.ruleset - false - - - none - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - false - - - - - - True - True - Strings.resx - - - - - - Designer - - - Designer - - - Designer - - - Designer - - - Designer - PublicResXFileCodeGenerator - Strings.Designer.cs - - - Designer - - - Designer - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 2.0 %28x86%29 - true - - - False - .NET Framework 3.0 %28x86%29 - false - - - False - .NET Framework 3.5 - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - - - - - \ No newline at end of file diff --git a/src/Translations/UILanguage.cs b/src/Translations/UILanguage.cs index f6a85069..d4bf3b83 100644 --- a/src/Translations/UILanguage.cs +++ b/src/Translations/UILanguage.cs @@ -42,7 +42,7 @@ static UILanguage() foreach (DirectoryInfo directory in directories) { - if (directory.Name != "Plugins" && directory.Name != "Templates") + if (directory.Name != "Plugins" && directory.Name != "Templates" && directory.Name != "styles") { string cultureName = directory.Name; UILanguage language = CreateUILanguage(cultureName);