diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f68d0e5c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,266 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# Create portable, custom editor settings with EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options + +# .NET coding convention settings for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference + +# Language conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions + +# Formatting conventions +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions + +# .NET naming conventions for EditorConfig +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions + +# Top-most EditorConfig file +root = true + +# Editor default newlines with a newline ending every file +[*] +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.json] +insert_final_newline = false + +[*.cs] +indent_size = 4 + +# Code files +[*.{cs,vb}] + +# .NET code style settings - "This." and "Me." qualifiers +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#this-and-me +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning + +# .NET code style settings - Language keywords instead of framework type names for type references +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#language-keywords +dotnet_style_predefined_type_for_locals_parameters_members = true:error +dotnet_style_predefined_type_for_member_access = true:error + +# .NET code style settings - Modifier preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#normalize-modifiers +dotnet_style_require_accessibility_modifiers = always:warning +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning +dotnet_style_readonly_field = true:warning + +# .NET code style settings - Parentheses preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parentheses-preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion + +# .NET code style settings - Expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-level-preferences +dotnet_style_object_initializer = true:error +dotnet_style_collection_initializer = true:error +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_compound_assignment = true:warning + +# .NET code style settings - Null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#null-checking-preferences +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:error + +# .NET code quality settings - Parameter preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#parameter-preferences +dotnet_code_quality_unused_parameters = all:warning + +# C# code style settings - Implicit and explicit types +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#implicit-and-explicit-types +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = true:warning +csharp_style_var_elsewhere = true:suggestion + +# C# code style settings - Expression-bodied members +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#expression-bodied-members +csharp_style_expression_bodied_methods = when_on_single_line:warning +csharp_style_expression_bodied_constructors = false:warning +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = when_on_single_line:warning +csharp_style_expression_bodied_local_functions = when_on_single_line:warning + +# C# code style settings - Pattern matching +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#pattern-matching +csharp_style_pattern_matching_over_is_with_cast_check = true:error +csharp_style_pattern_matching_over_as_with_null_check = true:error + +# C# code style settings - Inlined variable declaration +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#inlined-variable-declarations +csharp_style_inlined_variable_declaration = true:error + +# C# code style settings - C# expression-level preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-expression-level-preferences +csharp_prefer_simple_default_expression = true:suggestion + +# C# code style settings - C# null-checking preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#c-null-checking-preferences +csharp_style_throw_expression = true:warning +csharp_style_conditional_delegate_call = true:warning + +# C# code style settings - Code block preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#code-block-preferences +csharp_prefer_braces = when_multiline:suggestion + +# C# code style - Unused value preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#unused-value-preferences +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion + +# C# code style - Index and range preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#index-and-range-preferences +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion + +# C# code style - Miscellaneous preferences +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-language-conventions?view=vs-2019#miscellaneous-preferences +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_using_directive_placement = outside_namespace:warning +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = true:warning +csharp_style_prefer_switch_expression = true:warning + +# .NET formatting settings - Organize using directives +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#organize-using-directives +dotnet_sort_system_directives_first = true +dotnet_separate_import_directive_groups = false + +# C# formatting settings - New-line options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#new-line-options +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# C# formatting settings - Indentation options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#indentation-options +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents_when_block = false + +# C# formatting settings - Spacing options +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_after_comma = true +csharp_space_before_comma = false +csharp_space_after_dot = false +csharp_space_before_dot = false +csharp_space_after_semicolon_in_for_statement = true +csharp_space_before_semicolon_in_for_statement = false +csharp_space_around_declaration_statements = false +csharp_space_before_open_square_brackets = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_square_brackets = false + +# C# formatting settings - Wrap options +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-formatting-conventions?view=vs-2019#wrap-options +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +# C# formatting settings - Namespace options +csharp_style_namespace_declarations = file_scoped:error + +########## name all private fields using camelCase with underscore prefix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.private_fields_with_underscore.symbols = private_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private + +# dotnet_naming_rule..style = +dotnet_naming_rule.private_fields_with_underscore.style = prefix_underscore + +# dotnet_naming_style.. = +dotnet_naming_style.prefix_underscore.capitalization = camel_case +dotnet_naming_style.prefix_underscore.required_prefix = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.private_fields_with_underscore.severity = warning + +########## name all constant fields using UPPER_CASE ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.constant_fields_should_be_upper_case.symbols = constant_fields + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const + +# dotnet_naming_rule..style = +dotnet_naming_rule.constant_fields_should_be_upper_case.style = upper_case_style + +# dotnet_naming_style.. = +dotnet_naming_style.upper_case_style.capitalization = all_upper +dotnet_naming_style.upper_case_style.word_separator = _ + +# dotnet_naming_rule..severity = +dotnet_naming_rule.constant_fields_should_be_upper_case.severity = warning + +########## Async methods should have "Async" suffix ########## +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions?view=vs-2019 +# dotnet_naming_rule..symbols = +dotnet_naming_rule.async_methods_end_in_async.symbols = any_async_methods + +# dotnet_naming_symbols.. = +dotnet_naming_symbols.any_async_methods.applicable_kinds = method +dotnet_naming_symbols.any_async_methods.applicable_accessibilities = * +dotnet_naming_symbols.any_async_methods.required_modifiers = async + +# dotnet_naming_rule..style = +dotnet_naming_rule.async_methods_end_in_async.style = end_in_async_style + +# dotnet_naming_style.. = +dotnet_naming_style.end_in_async_style.capitalization = pascal_case +dotnet_naming_style.end_in_async_style.word_separator = +dotnet_naming_style.end_in_async_style.required_prefix = +dotnet_naming_style.end_in_async_style.required_suffix = Async + +# dotnet_naming_rule..severity = +dotnet_naming_rule.async_methods_end_in_async.severity = warning + +# Remove unnecessary import https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0005 +dotnet_diagnostic.IDE0005.severity = warning + +# Enforce formatting https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/formatting-rules#rule-id-ide0055-fix-formatting +dotnet_diagnostic.IDE0055.severity = error +dotnet_diagnostic.IDE1006.severity = error +dotnet_diagnostic.IDE0022.severity = error diff --git a/.github/workflows/wf-verify-formatting.yml b/.github/workflows/wf-verify-formatting.yml new file mode 100644 index 00000000..acc21c54 --- /dev/null +++ b/.github/workflows/wf-verify-formatting.yml @@ -0,0 +1,28 @@ +name: Format (Pull Request) +on: + pull_request: + branches: + - master + workflow_dispatch: + +jobs: + format: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v4 + name: Checkout Code + + - name: Install additional .NET SDKs + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 2.0.x + 5.0.x + 6.0.x + 8.0.x + + - name: Restore NuGet Packages + run: dotnet restore + + - name: Format solution + run: dotnet format --verify-no-changes --severity error diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..b2ffd159 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,8 @@ + + + 10 + true + $(WarningsNotAsErrors);IDE0005 + false + + diff --git a/QRCoder.Xaml/Properties/AssemblyInfo.cs b/QRCoder.Xaml/Properties/AssemblyInfo.cs index 86eade36..35379fae 100644 --- a/QRCoder.Xaml/Properties/AssemblyInfo.cs +++ b/QRCoder.Xaml/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +using System.Reflection; using System.Runtime.InteropServices; // Allgemeine Informationen über eine Assembly werden über die folgenden diff --git a/QRCoder.Xaml/QRCoder.Xaml.csproj b/QRCoder.Xaml/QRCoder.Xaml.csproj index dc33fa12..c72e7ef3 100644 --- a/QRCoder.Xaml/QRCoder.Xaml.csproj +++ b/QRCoder.Xaml/QRCoder.Xaml.csproj @@ -1,13 +1,12 @@ - + net35;net40;net5.0-windows;net6.0-windows false - true + true $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS - false + $(DefineConstants);NET6_0_WINDOWS true @@ -33,8 +32,8 @@ - - + + diff --git a/QRCoder.Xaml/XamlQRCode.cs b/QRCoder.Xaml/XamlQRCode.cs index 832beada..a94ce002 100644 --- a/QRCoder.Xaml/XamlQRCode.cs +++ b/QRCoder.Xaml/XamlQRCode.cs @@ -1,94 +1,87 @@ -using System; +using System; using System.Windows; using System.Windows.Media; using static QRCoder.QRCodeGenerator; -namespace QRCoder.Xaml +namespace QRCoder.Xaml; + +public class XamlQRCode : AbstractQRCode, IDisposable { - public class XamlQRCode : AbstractQRCode, IDisposable - { - /// - /// Constructor without params to be used in COM Objects connections - /// - public XamlQRCode() { } + /// + /// Constructor without params to be used in COM Objects connections + /// + public XamlQRCode() { } - public XamlQRCode(QRCodeData data) : base(data) { } + public XamlQRCode(QRCodeData data) : base(data) { } - public DrawingImage GetGraphic(int pixelsPerModule) - { - return this.GetGraphic(pixelsPerModule, true); - } + public DrawingImage GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, true); - public DrawingImage GetGraphic(int pixelsPerModule, bool drawQuietZones) - { - var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); - var viewBox = new Size(pixelsPerModule * drawableModulesCount, pixelsPerModule * drawableModulesCount); - return this.GetGraphic(viewBox, new SolidColorBrush(Colors.Black), new SolidColorBrush(Colors.White), drawQuietZones); - } + public DrawingImage GetGraphic(int pixelsPerModule, bool drawQuietZones) + { + var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); + var viewBox = new Size(pixelsPerModule * drawableModulesCount, pixelsPerModule * drawableModulesCount); + return GetGraphic(viewBox, new SolidColorBrush(Colors.Black), new SolidColorBrush(Colors.White), drawQuietZones); + } - public DrawingImage GetGraphic(Size viewBox, bool drawQuietZones = true) - { - return this.GetGraphic(viewBox, new SolidColorBrush(Colors.Black), new SolidColorBrush(Colors.White), drawQuietZones); - } + public DrawingImage GetGraphic(Size viewBox, bool drawQuietZones = true) + => GetGraphic(viewBox, new SolidColorBrush(Colors.Black), new SolidColorBrush(Colors.White), drawQuietZones); - public DrawingImage GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true) - { - var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); - var viewBox = new Size(pixelsPerModule * drawableModulesCount, pixelsPerModule * drawableModulesCount); - return this.GetGraphic(viewBox, new SolidColorBrush((Color)ColorConverter.ConvertFromString(darkColorHex)), new SolidColorBrush((Color)ColorConverter.ConvertFromString(lightColorHex)), drawQuietZones); - } + public DrawingImage GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true) + { + var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); + var viewBox = new Size(pixelsPerModule * drawableModulesCount, pixelsPerModule * drawableModulesCount); + return GetGraphic(viewBox, new SolidColorBrush((Color)ColorConverter.ConvertFromString(darkColorHex)), new SolidColorBrush((Color)ColorConverter.ConvertFromString(lightColorHex)), drawQuietZones); + } - public DrawingImage GetGraphic(Size viewBox, Brush darkBrush, Brush lightBrush, bool drawQuietZones = true) - { - var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); - var qrSize = Math.Min(viewBox.Width, viewBox.Height); - var unitsPerModule = qrSize / drawableModulesCount; - var offsetModules = drawQuietZones ? 0 : 4; + public DrawingImage GetGraphic(Size viewBox, Brush darkBrush, Brush lightBrush, bool drawQuietZones = true) + { + var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); + var qrSize = Math.Min(viewBox.Width, viewBox.Height); + var unitsPerModule = qrSize / drawableModulesCount; + var offsetModules = drawQuietZones ? 0 : 4; - DrawingGroup drawing = new DrawingGroup(); - drawing.Children.Add(new GeometryDrawing(lightBrush, null, new RectangleGeometry(new Rect(new Point(0, 0), new Size(qrSize, qrSize))))); + var drawing = new DrawingGroup(); + drawing.Children.Add(new GeometryDrawing(lightBrush, null, new RectangleGeometry(new Rect(new Point(0, 0), new Size(qrSize, qrSize))))); - var group = new GeometryGroup(); - double x = 0d, y = 0d; - for (int xi = offsetModules; xi < drawableModulesCount + offsetModules; xi++) + var group = new GeometryGroup(); + double x = 0d, y = 0d; + for (int xi = offsetModules; xi < drawableModulesCount + offsetModules; xi++) + { + y = 0d; + for (int yi = offsetModules; yi < drawableModulesCount + offsetModules; yi++) { - y = 0d; - for (int yi = offsetModules; yi < drawableModulesCount + offsetModules; yi++) + if (QrCodeData.ModuleMatrix[yi][xi]) { - if (this.QrCodeData.ModuleMatrix[yi][xi]) - { - group.Children.Add(new RectangleGeometry(new Rect(x, y, unitsPerModule, unitsPerModule))); - } - y += unitsPerModule; + group.Children.Add(new RectangleGeometry(new Rect(x, y, unitsPerModule, unitsPerModule))); } - x += unitsPerModule; + y += unitsPerModule; } - drawing.Children.Add(new GeometryDrawing(darkBrush, null, group)); - - return new DrawingImage(drawing); + x += unitsPerModule; } + drawing.Children.Add(new GeometryDrawing(darkBrush, null, group)); - private int GetDrawableModulesCount(bool drawQuietZones = true) - { - return this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8); - } + return new DrawingImage(drawing); + } - public double GetUnitsPerModule(Size viewBox, bool drawQuietZones = true) - { - var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); - var qrSize = Math.Min(viewBox.Width, viewBox.Height); - return qrSize / drawableModulesCount; - } + private int GetDrawableModulesCount(bool drawQuietZones = true) + => QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8); + + public double GetUnitsPerModule(Size viewBox, bool drawQuietZones = true) + { + var drawableModulesCount = GetDrawableModulesCount(drawQuietZones); + var qrSize = Math.Min(viewBox.Width, viewBox.Height); + return qrSize / drawableModulesCount; } +} - public static class XamlQRCodeHelper +public static class XamlQRCodeHelper +{ + public static DrawingImage GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true) { - public static DrawingImage GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new XamlQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new XamlQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones); } } diff --git a/QRCoder.sln b/QRCoder.sln index 3d38636f..49102939 100644 --- a/QRCoder.sln +++ b/QRCoder.sln @@ -19,7 +19,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QRCoderApiTests", "QRCoderA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QRCoderBenchmarks", "QRCoderBenchmarks\QRCoderBenchmarks.csproj", "{C33AB74A-2AB3-4BEA-A67F-EAB578E74E25}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QRCoderTrimAnalysis", "QRCoderTrimAnalysis\QRCoderTrimAnalysis.csproj", "{F046136A-7BEA-49F3-9415-70CE50AD533B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QRCoderTrimAnalysis", "QRCoderTrimAnalysis\QRCoderTrimAnalysis.csproj", "{F046136A-7BEA-49F3-9415-70CE50AD533B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A402660A-59F5-43E2-B5B0-69CE5E56DEA1}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + Directory.Build.props = Directory.Build.props + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -50,6 +56,7 @@ Global {AA6BE23A-7813-4D2A-835E-B673631AE9F1}.Release|x86.ActiveCfg = Release|Any CPU {AA6BE23A-7813-4D2A-835E-B673631AE9F1}.Release|x86.Build.0 = Release|Any CPU {014F04C6-6099-4552-9A4F-D09C6E39D576}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {014F04C6-6099-4552-9A4F-D09C6E39D576}.Debug|Any CPU.Build.0 = Debug|Any CPU {014F04C6-6099-4552-9A4F-D09C6E39D576}.Debug|ARM.ActiveCfg = Debug|Any CPU {014F04C6-6099-4552-9A4F-D09C6E39D576}.Debug|ARM.Build.0 = Debug|Any CPU {014F04C6-6099-4552-9A4F-D09C6E39D576}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -64,6 +71,7 @@ Global {014F04C6-6099-4552-9A4F-D09C6E39D576}.Release|x86.ActiveCfg = Release|Any CPU {014F04C6-6099-4552-9A4F-D09C6E39D576}.Release|x86.Build.0 = Release|Any CPU {CA6DB0F5-DB6C-4DDD-8B5F-EF82FBB963E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA6DB0F5-DB6C-4DDD-8B5F-EF82FBB963E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {CA6DB0F5-DB6C-4DDD-8B5F-EF82FBB963E9}.Debug|ARM.ActiveCfg = Debug|x86 {CA6DB0F5-DB6C-4DDD-8B5F-EF82FBB963E9}.Debug|x64.ActiveCfg = Debug|x86 {CA6DB0F5-DB6C-4DDD-8B5F-EF82FBB963E9}.Debug|x86.ActiveCfg = Debug|Any CPU diff --git a/QRCoder/ASCIIQRCode.cs b/QRCoder/ASCIIQRCode.cs index fc4b94e9..c84fab8f 100644 --- a/QRCoder/ASCIIQRCode.cs +++ b/QRCoder/ASCIIQRCode.cs @@ -1,151 +1,150 @@ -using System; +using System; using System.Collections.Generic; using System.Text; using static QRCoder.QRCodeGenerator; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents an ASCII-style QR code generator that provides functionality to render QR codes as textual representations. +/// +public class AsciiQRCode : AbstractQRCode, IDisposable { /// - /// Represents an ASCII-style QR code generator that provides functionality to render QR codes as textual representations. + /// Constructor without params to be used in COM Objects connections /// - public class AsciiQRCode : AbstractQRCode, IDisposable + public AsciiQRCode() { } + + public AsciiQRCode(QRCodeData data) : base(data) { } + + + /// + /// Returns a strings that contains the resulting QR code as textual representation. + /// + /// Number of repeated darkColorString/whiteSpaceString per module. + /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. + /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. + /// Bool that defines if quiet zones around the QR code shall be drawn + /// End of line separator. (Default: \n) + /// + public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n") { - /// - /// Constructor without params to be used in COM Objects connections - /// - public AsciiQRCode() { } - - public AsciiQRCode(QRCodeData data) : base(data) { } - - - /// - /// Returns a strings that contains the resulting QR code as textual representation. - /// - /// Number of repeated darkColorString/whiteSpaceString per module. - /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. - /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. - /// Bool that defines if quiet zones around the QR code shall be drawn - /// End of line separator. (Default: \n) - /// - public string GetGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true, string endOfLine = "\n") - { - if (repeatPerModule < 1) - throw new Exception("The repeatPerModule-parameter must be 1 or greater."); - return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones)); - } + if (repeatPerModule < 1) + throw new Exception("The repeatPerModule-parameter must be 1 or greater."); + return string.Join(endOfLine, GetLineByLineGraphic(repeatPerModule, darkColorString, whiteSpaceString, drawQuietZones)); + } - /// - /// Returns an array of strings that contains each line of the resulting QR code as ASCII chars. - /// - /// Number of repeated darkColorString/whiteSpaceString per module. - /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. - /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. - /// Bool that defines if quiet zones around the QR code shall be drawn - /// - public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true) + /// + /// Returns an array of strings that contains each line of the resulting QR code as ASCII chars. + /// + /// Number of repeated darkColorString/whiteSpaceString per module. + /// String for use as dark color modules. In case of string make sure whiteSpaceString has the same length. + /// String for use as white modules (whitespace). In case of string make sure darkColorString has the same length. + /// Bool that defines if quiet zones around the QR code shall be drawn + /// + public string[] GetLineByLineGraphic(int repeatPerModule, string darkColorString = "██", string whiteSpaceString = " ", bool drawQuietZones = true) + { + var qrCode = new List(); + //We need to adjust the repeatPerModule based on number of characters in darkColorString + //(we assume whiteSpaceString has the same number of characters) + //to keep the QR code as square as possible. + var quietZonesModifier = (drawQuietZones ? 0 : 8); + var quietZonesOffset = (int)(quietZonesModifier * 0.5); + var adjustmentValueForNumberOfCharacters = darkColorString.Length / 2 != 1 ? darkColorString.Length / 2 : 0; + var verticalNumberOfRepeats = repeatPerModule + adjustmentValueForNumberOfCharacters; + var sideLength = (QrCodeData.ModuleMatrix.Count - quietZonesModifier) * verticalNumberOfRepeats; + for (var y = 0; y < sideLength; y++) { - var qrCode = new List(); - //We need to adjust the repeatPerModule based on number of characters in darkColorString - //(we assume whiteSpaceString has the same number of characters) - //to keep the QR code as square as possible. - var quietZonesModifier = (drawQuietZones ? 0 : 8); - var quietZonesOffset = (int)(quietZonesModifier * 0.5); - var adjustmentValueForNumberOfCharacters = darkColorString.Length / 2 != 1 ? darkColorString.Length / 2 : 0; - var verticalNumberOfRepeats = repeatPerModule + adjustmentValueForNumberOfCharacters; - var sideLength = (QrCodeData.ModuleMatrix.Count - quietZonesModifier) * verticalNumberOfRepeats; - for (var y = 0; y < sideLength; y++) + var lineBuilder = new StringBuilder(); + for (var x = 0; x < QrCodeData.ModuleMatrix.Count - quietZonesModifier; x++) { - var lineBuilder = new StringBuilder(); - for (var x = 0; x < QrCodeData.ModuleMatrix.Count - quietZonesModifier; x++) + var module = QrCodeData.ModuleMatrix[((y + verticalNumberOfRepeats) / verticalNumberOfRepeats - 1) + quietZonesOffset][x + quietZonesOffset]; + for (var i = 0; i < repeatPerModule; i++) { - var module = QrCodeData.ModuleMatrix[((y + verticalNumberOfRepeats) / verticalNumberOfRepeats - 1)+quietZonesOffset][x + quietZonesOffset]; - for (var i = 0; i < repeatPerModule; i++) - { - lineBuilder.Append(module ? darkColorString : whiteSpaceString); - } + lineBuilder.Append(module ? darkColorString : whiteSpaceString); } - qrCode.Add(lineBuilder.ToString()); } - return qrCode.ToArray(); + qrCode.Add(lineBuilder.ToString()); } + return qrCode.ToArray(); + } - /// - /// Returns a strings that contains the resulting QR code as minified textual representation. - /// - /// Bool that defines if quiet zones around the QR code shall be drawn - /// If set to true, dark and light colors will be inverted - /// End of line separator. (Default: \n) - /// - public string GetGraphicSmall(bool drawQuietZones = true, bool invert = false, string endOfLine = "\n") - { - bool BLACK = true, WHITE = false; + /// + /// Returns a strings that contains the resulting QR code as minified textual representation. + /// + /// Bool that defines if quiet zones around the QR code shall be drawn + /// If set to true, dark and light colors will be inverted + /// End of line separator. (Default: \n) + /// + public string GetGraphicSmall(bool drawQuietZones = true, bool invert = false, string endOfLine = "\n") + { + bool BLACK = true, WHITE = false; - var palette = new - { - WHITE_ALL = "\u2588", - WHITE_BLACK = "\u2580", - BLACK_WHITE = "\u2584", - BLACK_ALL = " ", - }; + var palette = new + { + WHITE_ALL = "\u2588", + WHITE_BLACK = "\u2580", + BLACK_WHITE = "\u2584", + BLACK_ALL = " ", + }; - var moduleData = QrCodeData.ModuleMatrix; - var sbSize = (moduleData.Count + endOfLine.Length) * (int)Math.Ceiling(moduleData.Count / 2.0) - 1; - var lineBuilder = new StringBuilder(sbSize); + var moduleData = QrCodeData.ModuleMatrix; + var sbSize = (moduleData.Count + endOfLine.Length) * (int)Math.Ceiling(moduleData.Count / 2.0) - 1; + var lineBuilder = new StringBuilder(sbSize); - var quietZonesModifier = (drawQuietZones ? 0 : 8); - var quietZonesOffset = (int)(quietZonesModifier * 0.5); - var sideLength = (moduleData.Count - quietZonesModifier); + var quietZonesModifier = (drawQuietZones ? 0 : 8); + var quietZonesOffset = (int)(quietZonesModifier * 0.5); + var sideLength = (moduleData.Count - quietZonesModifier); - for (var row = 0; row < sideLength; row += 2) + for (var row = 0; row < sideLength; row += 2) + { + for (var col = 0; col < sideLength; col++) { - for (var col = 0; col < sideLength; col++) - { - var current = moduleData[col + quietZonesOffset][row + quietZonesOffset] ^ invert; - var nextRowId = row + quietZonesOffset + 1; - - // Set next to whitespace "color" - var next = BLACK; - // Fill next with value, if in data range - if (nextRowId < QrCodeData.ModuleMatrix.Count) - next = moduleData[col + quietZonesOffset][nextRowId] ^ invert; - - if (current == WHITE && next == WHITE) - lineBuilder.Append(palette.WHITE_ALL); - else if (current == WHITE && next == BLACK) - lineBuilder.Append(palette.WHITE_BLACK); - else if (current == BLACK && next == WHITE) - lineBuilder.Append(palette.BLACK_WHITE); - else - lineBuilder.Append(palette.BLACK_ALL); - } - if (row + 2 < sideLength) - lineBuilder.Append(endOfLine); + var current = moduleData[col + quietZonesOffset][row + quietZonesOffset] ^ invert; + var nextRowId = row + quietZonesOffset + 1; + + // Set next to whitespace "color" + var next = BLACK; + // Fill next with value, if in data range + if (nextRowId < QrCodeData.ModuleMatrix.Count) + next = moduleData[col + quietZonesOffset][nextRowId] ^ invert; + + if (current == WHITE && next == WHITE) + lineBuilder.Append(palette.WHITE_ALL); + else if (current == WHITE && next == BLACK) + lineBuilder.Append(palette.WHITE_BLACK); + else if (current == BLACK && next == WHITE) + lineBuilder.Append(palette.BLACK_WHITE); + else + lineBuilder.Append(palette.BLACK_ALL); } - return lineBuilder.ToString(); + if (row + 2 < sideLength) + lineBuilder.Append(endOfLine); } + return lineBuilder.ToString(); } +} - /// - /// Provides static methods for generating ASCII-style QR codes. - /// - public static class AsciiQRCodeHelper +/// +/// Provides static methods for generating ASCII-style QR codes. +/// +public static class AsciiQRCodeHelper +{ + public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorString, string whiteSpaceString, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true) { - public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorString, string whiteSpaceString, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new AsciiQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorString, whiteSpaceString, drawQuietZones, endOfLine); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new AsciiQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorString, whiteSpaceString, drawQuietZones, endOfLine); + } - public static string GetQRCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true, bool invert = true) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new AsciiQRCode(qrCodeData)) - return qrCode.GetGraphicSmall(drawQuietZones, invert, endOfLine); - } + public static string GetQRCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, string endOfLine = "\n", bool drawQuietZones = true, bool invert = true) + { + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new AsciiQRCode(qrCodeData); + return qrCode.GetGraphicSmall(drawQuietZones, invert, endOfLine); } } diff --git a/QRCoder/AbstractQRCode.cs b/QRCoder/AbstractQRCode.cs index e3833892..c544e3ea 100644 --- a/QRCoder/AbstractQRCode.cs +++ b/QRCoder/AbstractQRCode.cs @@ -1,47 +1,47 @@ -namespace QRCoder +namespace QRCoder; + +/// +/// Abstract base class for generating QR codes. +/// Derived classes typically render a QR code into a specific format (png, System.Drawing.Bitmap, PDF, etc). +/// +public abstract class AbstractQRCode { /// - /// Abstract base class for generating QR codes. - /// Derived classes typically render a QR code into a specific format (png, System.Drawing.Bitmap, PDF, etc). + /// Gets or sets the QRCodeData used to generate the QR code. /// - public abstract class AbstractQRCode - { - /// - /// Gets or sets the QRCodeData used to generate the QR code. - /// - protected QRCodeData QrCodeData { get; set; } + protected QRCodeData QrCodeData { get; set; } - /// - /// Initializes a new instance of the AbstractQRCode class. - /// - protected AbstractQRCode() { - this.QrCodeData = null!; - } + /// + /// Initializes a new instance of the AbstractQRCode class. + /// + protected AbstractQRCode() + { + QrCodeData = null!; + } - /// - /// Initializes a new instance of the AbstractQRCode class with the specified QRCodeData. - /// - /// The QRCodeData object generated by QRCodeGenerator.CreateQrCode(). - protected AbstractQRCode(QRCodeData data) { - this.QrCodeData = data; - } + /// + /// Initializes a new instance of the AbstractQRCode class with the specified QRCodeData. + /// + /// The QRCodeData object generated by QRCodeGenerator.CreateQrCode(). + protected AbstractQRCode(QRCodeData data) + { + QrCodeData = data; + } - /// - /// Sets the QRCodeData object that will be used to generate the QR code. - /// This method is useful for COM objects connections. - /// - /// The QRCodeData object generated by QRCodeGenerator.CreateQrCode(). - virtual public void SetQRCodeData(QRCodeData data) { - this.QrCodeData = data; - } + /// + /// Sets the QRCodeData object that will be used to generate the QR code. + /// This method is useful for COM objects connections. + /// + /// The QRCodeData object generated by QRCodeGenerator.CreateQrCode(). + public virtual void SetQRCodeData(QRCodeData data) + => QrCodeData = data; - /// - /// Disposes the QRCodeData object. - /// - public void Dispose() - { - this.QrCodeData?.Dispose(); - this.QrCodeData = null!; - } + /// + /// Disposes the QRCodeData object. + /// + public void Dispose() + { + QrCodeData?.Dispose(); + QrCodeData = null!; } } diff --git a/QRCoder/ArtQRCode.cs b/QRCoder/ArtQRCode.cs index fff7840c..09b8ad06 100644 --- a/QRCoder/ArtQRCode.cs +++ b/QRCoder/ArtQRCode.cs @@ -7,297 +7,286 @@ using static QRCoder.QRCodeGenerator; // pull request raised to extend library used. -namespace QRCoder +namespace QRCoder; + +/// +/// Represents an art-style QR code generator that provides functionality to render QR codes with dots as modules. +/// +#if NET6_0_OR_GREATER +[System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif +public class ArtQRCode : AbstractQRCode, IDisposable { /// - /// Represents an art-style QR code generator that provides functionality to render QR codes with dots as modules. + /// Initializes a new instance of the class. + /// Constructor without params to be used in COM Objects connections /// -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif - public class ArtQRCode : AbstractQRCode, IDisposable - { - /// - /// Initializes a new instance of the class. - /// Constructor without params to be used in COM Objects connections - /// - public ArtQRCode() { } - - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the . - public ArtQRCode(QRCodeData data) : base(data) { } - - /// - /// Renders an art-style QR code with dots as modules. (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true) - /// - /// Amount of px each dark/light module of the QR code shall take place in the final QR code image - /// QRCode graphic as bitmap - public Bitmap GetGraphic(int pixelsPerModule) - { - return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, Color.Transparent); - } + public ArtQRCode() { } - /// - /// Renders an art-style QR code with dots as modules and a background image (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true) - /// - /// A bitmap object that will be used as background picture - /// QRCode graphic as bitmap - public Bitmap GetGraphic(Bitmap? backgroundImage = null) - { - return this.GetGraphic(10, Color.Black, Color.White, Color.Transparent, backgroundImage: backgroundImage); - } + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the . + public ArtQRCode(QRCodeData data) : base(data) { } - /// - /// Renders an art-style QR code with dots as modules and various user settings - /// - /// Amount of px each dark/light module of the QR code shall take place in the final QR code image - /// Color of the dark modules - /// Color of the light modules - /// Color of the background - /// A bitmap object that will be used as background picture - /// Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be. - /// If true a white border is drawn around the whole QR Code - /// Style of the quiet zones - /// Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone - /// Optional image that should be used instead of the default finder patterns - /// QRCode graphic as bitmap - public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, Bitmap? backgroundImage = null, double pixelSizeFactor = 1, - bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Dotted, - BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap? finderPatternImage = null) - { - if (pixelSizeFactor > 1) - throw new Exception("The parameter pixelSize must be between 0 and 1. (0-100%)"); - int pixelSize = (int)Math.Min(pixelsPerModule, Math.Floor(pixelsPerModule * pixelSizeFactor)); + /// + /// Renders an art-style QR code with dots as modules. (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true) + /// + /// Amount of px each dark/light module of the QR code shall take place in the final QR code image + /// QRCode graphic as bitmap + public Bitmap GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, Color.Black, Color.White, Color.Transparent); - var numModules = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8); - var offset = (drawQuietZones ? 0 : 4); - var size = numModules * pixelsPerModule; + /// + /// Renders an art-style QR code with dots as modules and a background image (With default settings: DarkColor=Black, LightColor=White, Background=Transparent, QuietZone=true) + /// + /// A bitmap object that will be used as background picture + /// QRCode graphic as bitmap + public Bitmap GetGraphic(Bitmap? backgroundImage = null) + => GetGraphic(10, Color.Black, Color.White, Color.Transparent, backgroundImage: backgroundImage); - var bitmap = new Bitmap(size, size); + /// + /// Renders an art-style QR code with dots as modules and various user settings + /// + /// Amount of px each dark/light module of the QR code shall take place in the final QR code image + /// Color of the dark modules + /// Color of the light modules + /// Color of the background + /// A bitmap object that will be used as background picture + /// Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be. + /// If true a white border is drawn around the whole QR Code + /// Style of the quiet zones + /// Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone + /// Optional image that should be used instead of the default finder patterns + /// QRCode graphic as bitmap + public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, Bitmap? backgroundImage = null, double pixelSizeFactor = 1, + bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Dotted, + BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap? finderPatternImage = null) + { + if (pixelSizeFactor > 1) + throw new Exception("The parameter pixelSize must be between 0 and 1. (0-100%)"); + int pixelSize = (int)Math.Min(pixelsPerModule, Math.Floor(pixelsPerModule * pixelSizeFactor)); + + var numModules = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8); + var offset = (drawQuietZones ? 0 : 4); + var size = numModules * pixelsPerModule; + + var bitmap = new Bitmap(size, size); - using (var graphics = Graphics.FromImage(bitmap)) + using (var graphics = Graphics.FromImage(bitmap)) + { + using var lightBrush = new SolidBrush(lightColor); + using var darkBrush = new SolidBrush(darkColor); + // make background transparent + using (var brush = new SolidBrush(backgroundColor)) + graphics.FillRectangle(brush, new Rectangle(0, 0, size, size)); + //Render background if set + if (backgroundImage != null) { - using (var lightBrush = new SolidBrush(lightColor)) + if (backgroundImageStyle == BackgroundImageStyle.Fill) + graphics.DrawImage(Resize(backgroundImage, size), 0, 0); + else if (backgroundImageStyle == BackgroundImageStyle.DataAreaOnly) { - using (var darkBrush = new SolidBrush(darkColor)) - { - // make background transparent - using (var brush = new SolidBrush(backgroundColor)) - graphics.FillRectangle(brush, new Rectangle(0, 0, size, size)); - //Render background if set - if (backgroundImage != null) - { - if (backgroundImageStyle == BackgroundImageStyle.Fill) - graphics.DrawImage(Resize(backgroundImage, size), 0, 0); - else if (backgroundImageStyle == BackgroundImageStyle.DataAreaOnly) - { - var bgOffset = 4 - offset; - graphics.DrawImage(Resize(backgroundImage, size - (2 * bgOffset * pixelsPerModule)), 0 + (bgOffset * pixelsPerModule), (bgOffset * pixelsPerModule)); - } - } - - - var darkModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, darkBrush); - var lightModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, lightBrush); - - for (var x = 0; x < numModules; x += 1) - { - for (var y = 0; y < numModules; y += 1) - { - var rectangleF = new Rectangle(x * pixelsPerModule, y * pixelsPerModule, pixelsPerModule, pixelsPerModule); - - var pixelIsDark = this.QrCodeData.ModuleMatrix[offset + y][offset + x]; - var solidBrush = pixelIsDark ? darkBrush : lightBrush; - var pixelImage = pixelIsDark ? darkModulePixel : lightModulePixel; - - if (!IsPartOfFinderPattern(x, y, numModules, offset)) - if (drawQuietZones && quietZoneRenderingStyle == QuietZoneStyle.Flat && IsPartOfQuietZone(x, y, numModules)) - graphics.FillRectangle(solidBrush, rectangleF); - else - graphics.DrawImage(pixelImage, rectangleF); - else if (finderPatternImage == null) - graphics.FillRectangle(solidBrush, rectangleF); - } - } - if (finderPatternImage != null) - { - var finderPatternSize = 7 * pixelsPerModule; - var finderPatternOffset = drawQuietZones ? 4 * pixelsPerModule : 0; - graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, finderPatternOffset, finderPatternSize, finderPatternSize)); - graphics.DrawImage(finderPatternImage, new Rectangle(size - finderPatternOffset - finderPatternSize, finderPatternOffset, finderPatternSize, finderPatternSize)); - graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, size - finderPatternOffset - finderPatternSize, finderPatternSize, finderPatternSize)); - } - graphics.Save(); - } + var bgOffset = 4 - offset; + graphics.DrawImage(Resize(backgroundImage, size - (2 * bgOffset * pixelsPerModule)), 0 + (bgOffset * pixelsPerModule), (bgOffset * pixelsPerModule)); } } - return bitmap; - } - /// - /// If the pixelSize is bigger than the pixelsPerModule or may end up filling the Module making a traditional QR code. - /// - /// Pixels used per module rendered - /// Size of the dots - /// Color of the pixels - /// - private Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush) - { - // draw a dot - var bitmap = new Bitmap(pixelSize, pixelSize); - using (var graphics = Graphics.FromImage(bitmap)) + + var darkModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, darkBrush); + var lightModulePixel = MakeDotPixel(pixelsPerModule, pixelSize, lightBrush); + + for (var x = 0; x < numModules; x += 1) { - graphics.FillEllipse(brush, new Rectangle(0, 0, pixelSize, pixelSize)); - graphics.Save(); - } + for (var y = 0; y < numModules; y += 1) + { + var rectangleF = new Rectangle(x * pixelsPerModule, y * pixelsPerModule, pixelsPerModule, pixelsPerModule); - var pixelWidth = Math.Min(pixelsPerModule, pixelSize); - var margin = Math.Max((pixelsPerModule - pixelWidth) / 2, 0); + var pixelIsDark = QrCodeData.ModuleMatrix[offset + y][offset + x]; + var solidBrush = pixelIsDark ? darkBrush : lightBrush; + var pixelImage = pixelIsDark ? darkModulePixel : lightModulePixel; - // center the dot in the module and crop to stay the right size. - var cropped = new Bitmap(pixelsPerModule, pixelsPerModule); - using (var graphics = Graphics.FromImage(cropped)) + if (!IsPartOfFinderPattern(x, y, numModules, offset)) + if (drawQuietZones && quietZoneRenderingStyle == QuietZoneStyle.Flat && IsPartOfQuietZone(x, y, numModules)) + graphics.FillRectangle(solidBrush, rectangleF); + else + graphics.DrawImage(pixelImage, rectangleF); + else if (finderPatternImage == null) + graphics.FillRectangle(solidBrush, rectangleF); + } + } + if (finderPatternImage != null) { - graphics.DrawImage(bitmap, new Rectangle(margin, margin, pixelWidth, pixelWidth), - new RectangleF(((float)pixelSize - pixelWidth) / 2, ((float)pixelSize - pixelWidth) / 2, pixelWidth, pixelWidth), - GraphicsUnit.Pixel); - graphics.Save(); + var finderPatternSize = 7 * pixelsPerModule; + var finderPatternOffset = drawQuietZones ? 4 * pixelsPerModule : 0; + graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, finderPatternOffset, finderPatternSize, finderPatternSize)); + graphics.DrawImage(finderPatternImage, new Rectangle(size - finderPatternOffset - finderPatternSize, finderPatternOffset, finderPatternSize, finderPatternSize)); + graphics.DrawImage(finderPatternImage, new Rectangle(finderPatternOffset, size - finderPatternOffset - finderPatternSize, finderPatternSize, finderPatternSize)); } - - return cropped; + graphics.Save(); } + return bitmap; + } - - /// - /// Checks if a given module(-position) is part of the quietzone of a QR code - /// - /// X position - /// Y position - /// Total number of modules per row - /// true, if position is part of quiet zone - private bool IsPartOfQuietZone(int x, int y, int numModules) + /// + /// If the pixelSize is bigger than the pixelsPerModule or may end up filling the Module making a traditional QR code. + /// + /// Pixels used per module rendered + /// Size of the dots + /// Color of the pixels + /// + private Bitmap MakeDotPixel(int pixelsPerModule, int pixelSize, SolidBrush brush) + { + // draw a dot + var bitmap = new Bitmap(pixelSize, pixelSize); + using (var graphics = Graphics.FromImage(bitmap)) { - return - x < 4 || //left - y < 4 || //top - x > numModules - 5 || //right - y > numModules - 5; //bottom + graphics.FillEllipse(brush, new Rectangle(0, 0, pixelSize, pixelSize)); + graphics.Save(); } + var pixelWidth = Math.Min(pixelsPerModule, pixelSize); + var margin = Math.Max((pixelsPerModule - pixelWidth) / 2, 0); - /// - /// Checks if a given module(-position) is part of one of the three finder patterns of a QR code - /// - /// X position - /// Y position - /// Total number of modules per row - /// Offset in modules (usually depending on drawQuietZones parameter) - /// true, if position is part of any finder pattern - private bool IsPartOfFinderPattern(int x, int y, int numModules, int offset) + // center the dot in the module and crop to stay the right size. + var cropped = new Bitmap(pixelsPerModule, pixelsPerModule); + using (var graphics = Graphics.FromImage(cropped)) { - var cornerSize = 11 - offset; - var outerLimitLow = (numModules - cornerSize - 1); - var outerLimitHigh = outerLimitLow + 8; - var invertedOffset = 4 - offset; - return - (x >= invertedOffset && x < cornerSize && y >= invertedOffset && y < cornerSize) || //Top-left finder pattern - (x > outerLimitLow && x < outerLimitHigh && y >= invertedOffset && y < cornerSize) || //Top-right finder pattern - (x >= invertedOffset && x < cornerSize && y > outerLimitLow && y < outerLimitHigh); //Bottom-left finder pattern + graphics.DrawImage(bitmap, new Rectangle(margin, margin, pixelWidth, pixelWidth), + new RectangleF(((float)pixelSize - pixelWidth) / 2, ((float)pixelSize - pixelWidth) / 2, pixelWidth, pixelWidth), + GraphicsUnit.Pixel); + graphics.Save(); } - /// - /// Resize to a square bitmap, but maintain the aspect ratio by padding transparently. - /// - /// - /// - /// Resized image as bitmap - private Bitmap Resize(Bitmap image, int newSize) - { - float scale = Math.Min((float)newSize / image.Width, (float)newSize / image.Height); - var scaledWidth = (int)(image.Width * scale); - var scaledHeight = (int)(image.Height * scale); - var offsetX = (newSize - scaledWidth) / 2; - var offsetY = (newSize - scaledHeight) / 2; + return cropped; + } - var scaledImage = new Bitmap(image, new Size(scaledWidth, scaledHeight)); - var bm = new Bitmap(newSize, newSize); + /// + /// Checks if a given module(-position) is part of the quietzone of a QR code + /// + /// X position + /// Y position + /// Total number of modules per row + /// true, if position is part of quiet zone + private bool IsPartOfQuietZone(int x, int y, int numModules) + { + return + x < 4 || //left + y < 4 || //top + x > numModules - 5 || //right + y > numModules - 5; //bottom + } - using (Graphics graphics = Graphics.FromImage(bm)) - { - using (var brush = new SolidBrush(Color.Transparent)) - { - graphics.FillRectangle(brush, new Rectangle(0, 0, newSize, newSize)); - graphics.InterpolationMode = InterpolationMode.High; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.SmoothingMode = SmoothingMode.AntiAlias; + /// + /// Checks if a given module(-position) is part of one of the three finder patterns of a QR code + /// + /// X position + /// Y position + /// Total number of modules per row + /// Offset in modules (usually depending on drawQuietZones parameter) + /// true, if position is part of any finder pattern + private bool IsPartOfFinderPattern(int x, int y, int numModules, int offset) + { + var cornerSize = 11 - offset; + var outerLimitLow = (numModules - cornerSize - 1); + var outerLimitHigh = outerLimitLow + 8; + var invertedOffset = 4 - offset; + return + (x >= invertedOffset && x < cornerSize && y >= invertedOffset && y < cornerSize) || //Top-left finder pattern + (x > outerLimitLow && x < outerLimitHigh && y >= invertedOffset && y < cornerSize) || //Top-right finder pattern + (x >= invertedOffset && x < cornerSize && y > outerLimitLow && y < outerLimitHigh); //Bottom-left finder pattern + } - graphics.DrawImage(scaledImage, new Rectangle(offsetX, offsetY, scaledWidth, scaledHeight)); - } - } - return bm; - } + /// + /// Resize to a square bitmap, but maintain the aspect ratio by padding transparently. + /// + /// + /// + /// Resized image as bitmap + private Bitmap Resize(Bitmap image, int newSize) + { + float scale = Math.Min((float)newSize / image.Width, (float)newSize / image.Height); + var scaledWidth = (int)(image.Width * scale); + var scaledHeight = (int)(image.Height * scale); + var offsetX = (newSize - scaledWidth) / 2; + var offsetY = (newSize - scaledHeight) / 2; - /// - /// Defines how the quiet zones shall be rendered. - /// - public enum QuietZoneStyle - { - Dotted, - Flat - } + var scaledImage = new Bitmap(image, new Size(scaledWidth, scaledHeight)); + + var bm = new Bitmap(newSize, newSize); - /// - /// Defines how the background image (if set) shall be rendered. - /// - public enum BackgroundImageStyle + using (var graphics = Graphics.FromImage(bm)) { - Fill, - DataAreaOnly + using var brush = new SolidBrush(Color.Transparent); + graphics.FillRectangle(brush, new Rectangle(0, 0, newSize, newSize)); + + graphics.InterpolationMode = InterpolationMode.High; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.SmoothingMode = SmoothingMode.AntiAlias; + + graphics.DrawImage(scaledImage, new Rectangle(offsetX, offsetY, scaledWidth, scaledHeight)); } + return bm; + } + + /// + /// Defines how the quiet zones shall be rendered. + /// + public enum QuietZoneStyle + { + Dotted, + Flat } /// - /// Provides static methods for creating art-style QR codes. + /// Defines how the background image (if set) shall be rendered. /// + public enum BackgroundImageStyle + { + Fill, + DataAreaOnly + } +} + +/// +/// Provides static methods for creating art-style QR codes. +/// #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] +[System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public static class ArtQRCodeHelper +public static class ArtQRCodeHelper +{ + /// + /// Helper function to create an ArtQRCode graphic with a single function call + /// + /// Text/payload to be encoded inside the QR code + /// Amount of px each dark/light module of the QR code shall take place in the final QR code image + /// Color of the dark modules + /// Color of the light modules + /// Color of the background + /// The level of error correction data + /// Shall the generator be forced to work in UTF-8 mode? + /// Should the byte-order-mark be used? + /// Which ECI mode shall be used? + /// Set fixed QR code target version. + /// A bitmap object that will be used as background picture + /// Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be. + /// If true a white border is drawn around the whole QR Code + /// Style of the quiet zones + /// Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone + /// Optional image that should be used instead of the default finder patterns + /// QRCode graphic as bitmap + public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, ECCLevel eccLevel, bool forceUtf8 = false, + bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap? backgroundImage = null, double pixelSizeFactor = 1.0, + bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Flat, + BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap? finderPatternImage = null) { - /// - /// Helper function to create an ArtQRCode graphic with a single function call - /// - /// Text/payload to be encoded inside the QR code - /// Amount of px each dark/light module of the QR code shall take place in the final QR code image - /// Color of the dark modules - /// Color of the light modules - /// Color of the background - /// The level of error correction data - /// Shall the generator be forced to work in UTF-8 mode? - /// Should the byte-order-mark be used? - /// Which ECI mode shall be used? - /// Set fixed QR code target version. - /// A bitmap object that will be used as background picture - /// Value between 0.0 to 1.0 that defines how big the module dots are. The bigger the value, the less round the dots will be. - /// If true a white border is drawn around the whole QR Code - /// Style of the quiet zones - /// Style of the background image (if set). Fill=spanning complete graphic; DataAreaOnly=Don't paint background into quietzone - /// Optional image that should be used instead of the default finder patterns - /// QRCode graphic as bitmap - public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, Color backgroundColor, ECCLevel eccLevel, bool forceUtf8 = false, - bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap? backgroundImage = null, double pixelSizeFactor = 1.0, - bool drawQuietZones = true, QuietZoneStyle quietZoneRenderingStyle = QuietZoneStyle.Flat, - BackgroundImageStyle backgroundImageStyle = BackgroundImageStyle.DataAreaOnly, Bitmap? finderPatternImage = null) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new ArtQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, backgroundColor, backgroundImage, pixelSizeFactor, drawQuietZones, quietZoneRenderingStyle, backgroundImageStyle, finderPatternImage); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new ArtQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, backgroundColor, backgroundImage, pixelSizeFactor, drawQuietZones, quietZoneRenderingStyle, backgroundImageStyle, finderPatternImage); } } diff --git a/QRCoder/Attributes/NotNullWhenAttribute.cs b/QRCoder/Attributes/NotNullWhenAttribute.cs index e0aa2a4c..3cc3ce1c 100644 --- a/QRCoder/Attributes/NotNullWhenAttribute.cs +++ b/QRCoder/Attributes/NotNullWhenAttribute.cs @@ -1,27 +1,26 @@ -#if !NETCOREAPP -namespace System.Diagnostics.CodeAnalysis +#if !NETCOREAPP +namespace System.Diagnostics.CodeAnalysis; + +/// +/// Specifies that when a method returns System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue, +/// the parameter will not be null even if the corresponding type allows it. +/// +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute { /// - /// Specifies that when a method returns System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue, - /// the parameter will not be null even if the corresponding type allows it. + /// Initializes the attribute with the specified return value condition. /// - [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] - internal sealed class NotNullWhenAttribute : Attribute + /// If the method returns this value, the associated parameter will not be null. + public NotNullWhenAttribute(bool returnValue) { - /// - /// Initializes the attribute with the specified return value condition. - /// - /// If the method returns this value, the associated parameter will not be null. - public NotNullWhenAttribute(bool returnValue) - { - ReturnValue = returnValue; - } - - /// - /// Gets the return value condition. If the method returns this value, the associated - /// parameter will not be null. - /// - public bool ReturnValue { get; } + ReturnValue = returnValue; } + + /// + /// Gets the return value condition. If the method returns this value, the associated + /// parameter will not be null. + /// + public bool ReturnValue { get; } } #endif diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 799be2b7..b6619b03 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -1,4 +1,4 @@ -#if !NETSTANDARD1_3 +#if !NETSTANDARD1_3 using System; using System.Drawing; using System.Drawing.Imaging; @@ -7,225 +7,210 @@ using static QRCoder.Base64QRCode; using static QRCoder.QRCodeGenerator; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents a QR code generator that outputs base64-encoded images. +/// +public class Base64QRCode : AbstractQRCode, IDisposable { /// - /// Represents a QR code generator that outputs base64-encoded images. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. /// - public class Base64QRCode : AbstractQRCode, IDisposable + public Base64QRCode() { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public Base64QRCode() { - } + } - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public Base64QRCode(QRCodeData data) : base(data) { - } + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public Base64QRCode(QRCodeData data) : base(data) + { + } - /// - /// Returns a base64-encoded string that contains the resulting QR code as a PNG image. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a base64-encoded string. - public string GetGraphic(int pixelsPerModule) - { - return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true); - } + /// + /// Returns a base64-encoded string that contains the resulting QR code as a PNG image. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a base64-encoded string. + public string GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, Color.Black, Color.White, true); - /// - /// Returns a base64-encoded string that contains the resulting QR code as an image. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// Indicates if quiet zones around the QR code should be drawn. - /// The type of image to generate (PNG, JPEG, GIF). - /// Returns the QR code graphic as a base64-encoded string. - public string GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true, ImageType imgType = ImageType.Png) - { - return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones, imgType); - } + /// + /// Returns a base64-encoded string that contains the resulting QR code as an image. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// Indicates if quiet zones around the QR code should be drawn. + /// The type of image to generate (PNG, JPEG, GIF). + /// Returns the QR code graphic as a base64-encoded string. + public string GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true, ImageType imgType = ImageType.Png) + => GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones, imgType); - /// - /// Returns a base64-encoded string that contains the resulting QR code as an image. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// Indicates if quiet zones around the QR code should be drawn. - /// The type of image to generate (PNG, JPEG, GIF). - /// Returns the QR code graphic as a base64-encoded string. - public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, ImageType imgType = ImageType.Png) + /// + /// Returns a base64-encoded string that contains the resulting QR code as an image. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// Indicates if quiet zones around the QR code should be drawn. + /// The type of image to generate (PNG, JPEG, GIF). + /// Returns the QR code graphic as a base64-encoded string. + public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, ImageType imgType = ImageType.Png) + { + if (imgType == ImageType.Png) { - if (imgType == ImageType.Png) - { - var pngCoder = new PngByteQRCode(QrCodeData); + var pngCoder = new PngByteQRCode(QrCodeData); - byte[] pngData; - if (darkColor == Color.Black && lightColor == Color.White) + byte[] pngData; + if (darkColor == Color.Black && lightColor == Color.White) + { + pngData = pngCoder.GetGraphic(pixelsPerModule, drawQuietZones); + } + else + { + byte[] darkColorBytes; + byte[] lightColorBytes; + if (darkColor.A != 255 || lightColor.A != 255) { - pngData = pngCoder.GetGraphic(pixelsPerModule, drawQuietZones); + darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B, darkColor.A }; + lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B, lightColor.A }; } else { - byte[] darkColorBytes; - byte[] lightColorBytes; - if (darkColor.A != 255 || lightColor.A != 255) - { - darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B, darkColor.A }; - lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B, lightColor.A }; - } - else - { - darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B }; - lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B }; - } - pngData = pngCoder.GetGraphic(pixelsPerModule, darkColorBytes, lightColorBytes, drawQuietZones); + darkColorBytes = new byte[] { darkColor.R, darkColor.G, darkColor.B }; + lightColorBytes = new byte[] { lightColor.R, lightColor.G, lightColor.B }; } - - return Convert.ToBase64String(pngData, Base64FormattingOptions.None); + pngData = pngCoder.GetGraphic(pixelsPerModule, darkColorBytes, lightColorBytes, drawQuietZones); } + return Convert.ToBase64String(pngData, Base64FormattingOptions.None); + } + #if SYSTEM_DRAWING #pragma warning disable CA1416 // Validate platform compatibility - var qr = new QRCode(QrCodeData); - var base64 = string.Empty; - using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) - { - base64 = BitmapToBase64(bmp, imgType); - } - return base64; + var qr = new QRCode(QrCodeData); + var base64 = string.Empty; + using (var bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones)) + { + base64 = BitmapToBase64(bmp, imgType); + } + return base64; #pragma warning restore CA1416 // Validate platform compatibility #else - throw new PlatformNotSupportedException("Only the PNG image type is supported on this platform."); + throw new PlatformNotSupportedException("Only the PNG image type is supported on this platform."); #endif - } + } #if SYSTEM_DRAWING - /// - /// Returns a base64-encoded string that contains the resulting QR code as an image with an embedded icon. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// The icon to embed in the center of the QR code. - /// The size of the icon as a percentage of the QR code. - /// The width of the border around the icon. - /// Indicates if quiet zones around the QR code should be drawn. - /// The type of image to generate (PNG, JPEG, GIF). - /// Returns the QR code graphic as a base64-encoded string. + /// + /// Returns a base64-encoded string that contains the resulting QR code as an image with an embedded icon. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// The icon to embed in the center of the QR code. + /// The size of the icon as a percentage of the QR code. + /// The width of the border around the icon. + /// Indicates if quiet zones around the QR code should be drawn. + /// The type of image to generate (PNG, JPEG, GIF). + /// Returns the QR code graphic as a base64-encoded string. #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png) + public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png) + { + var qr = new QRCode(QrCodeData); + var base64 = string.Empty; + using (var bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones)) { - var qr = new QRCode(QrCodeData); - var base64 = string.Empty; - using (Bitmap bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones)) - { - base64 = BitmapToBase64(bmp, imgType); - } - return base64; + base64 = BitmapToBase64(bmp, imgType); } + return base64; + } #endif #if SYSTEM_DRAWING - /// - /// Converts a bitmap to a base64-encoded string. - /// - /// The bitmap to convert. - /// The type of image (PNG, JPEG, GIF). - /// Returns the base64-encoded string representation of the bitmap. + /// + /// Converts a bitmap to a base64-encoded string. + /// + /// The bitmap to convert. + /// The type of image (PNG, JPEG, GIF). + /// Returns the base64-encoded string representation of the bitmap. #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - private string BitmapToBase64(Bitmap bmp, ImageType imgType) + private string BitmapToBase64(Bitmap bmp, ImageType imgType) + { + var iFormat = imgType switch { - var base64 = string.Empty; - ImageFormat iFormat; - switch (imgType) { - case ImageType.Png: - iFormat = ImageFormat.Png; - break; - case ImageType.Jpeg: - iFormat = ImageFormat.Jpeg; - break; - case ImageType.Gif: - iFormat = ImageFormat.Gif; - break; - default: - iFormat = ImageFormat.Png; - break; - } - using (MemoryStream memoryStream = new MemoryStream()) - { - bmp.Save(memoryStream, iFormat); - base64 = Convert.ToBase64String(memoryStream.ToArray(), Base64FormattingOptions.None); - } - return base64; - } + ImageType.Png => ImageFormat.Png, + ImageType.Jpeg => ImageFormat.Jpeg, + ImageType.Gif => ImageFormat.Gif, + _ => ImageFormat.Png, + }; + using var memoryStream = new MemoryStream(); + bmp.Save(memoryStream, iFormat); + return Convert.ToBase64String(memoryStream.ToArray(), Base64FormattingOptions.None); + } #endif - /// - /// Specifies the type of image to generate. - /// - public enum ImageType - { + /// + /// Specifies the type of image to generate. + /// + public enum ImageType + { #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - /// - /// GIF image format. - /// - Gif, + /// + /// GIF image format. + /// + Gif, #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - /// - /// JPEG image format. - /// - Jpeg, - /// - /// PNG image format. - /// - Png - } - + /// + /// JPEG image format. + /// + Jpeg, + /// + /// PNG image format. + /// + Png } +} + +/// +/// Provides static methods for creating base64-encoded QR codes. +/// +public static class Base64QRCodeHelper +{ /// - /// Provides static methods for creating base64-encoded QR codes. + /// Creates a base64-encoded QR code with a single function call. /// - public static class Base64QRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Indicates if quiet zones around the QR code should be drawn. + /// The type of image to generate (PNG, JPEG, GIF). + /// Returns the QR code graphic as a base64-encoded string. + public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png) { - /// - /// Creates a base64-encoded QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Indicates if quiet zones around the QR code should be drawn. - /// The type of image to generate (PNG, JPEG, GIF). - /// Returns the QR code graphic as a base64-encoded string. - public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new Base64QRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex, drawQuietZones, imgType); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new Base64QRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex, drawQuietZones, imgType); } } diff --git a/QRCoder/BitmapByteQRCode.cs b/QRCoder/BitmapByteQRCode.cs index feecdd9a..40045e5d 100644 --- a/QRCoder/BitmapByteQRCode.cs +++ b/QRCoder/BitmapByteQRCode.cs @@ -1,185 +1,179 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using static QRCoder.QRCodeGenerator; -namespace QRCoder +namespace QRCoder; + + +/// +/// Represents a QR code generator that outputs QR codes as bitmap byte arrays. +/// +// ReSharper disable once InconsistentNaming +public class BitmapByteQRCode : AbstractQRCode, IDisposable { + /// + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. + /// + public BitmapByteQRCode() { } /// - /// Represents a QR code generator that outputs QR codes as bitmap byte arrays. + /// Initializes a new instance of the class with the specified . /// - // ReSharper disable once InconsistentNaming - public class BitmapByteQRCode : AbstractQRCode, IDisposable - { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public BitmapByteQRCode() { } - - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public BitmapByteQRCode(QRCodeData data) : base(data) { } - - /// - /// Returns the QR code graphic as a bitmap byte array. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a bitmap byte array. - public byte[] GetGraphic(int pixelsPerModule) - { - return GetGraphic(pixelsPerModule, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0xFF, 0xFF, 0xFF }); - } + /// generated by the QRCodeGenerator. + public BitmapByteQRCode(QRCodeData data) : base(data) { } - /// - /// Returns the QR code graphic as a bitmap byte array. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// Returns the QR code graphic as a bitmap byte array. - public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex) - { - return GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex)); - } + /// + /// Returns the QR code graphic as a bitmap byte array. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a bitmap byte array. + public byte[] GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0xFF, 0xFF, 0xFF }); - /// - /// Returns the QR code graphic as a bitmap byte array. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules as an RGB byte array. - /// The color of the light modules as an RGB byte array. - /// Returns the QR code graphic as a bitmap byte array. - public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgb, byte[] lightColorRgb) - { - var sideLength = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule; + /// + /// Returns the QR code graphic as a bitmap byte array. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// Returns the QR code graphic as a bitmap byte array. + public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex) + => GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex)); + + /// + /// Returns the QR code graphic as a bitmap byte array. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules as an RGB byte array. + /// The color of the light modules as an RGB byte array. + /// Returns the QR code graphic as a bitmap byte array. + public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgb, byte[] lightColorRgb) + { + var sideLength = QrCodeData.ModuleMatrix.Count * pixelsPerModule; - var moduleDark = darkColorRgb.Reverse(); - var moduleLight = lightColorRgb.Reverse(); + var moduleDark = darkColorRgb.Reverse(); + var moduleLight = lightColorRgb.Reverse(); - List bmp = new List(); + var bmp = new List(); - //header - bmp.AddRange(new byte[] { 0x42, 0x4D, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 }); + //header + bmp.AddRange(new byte[] { 0x42, 0x4D, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 }); - //width - bmp.AddRange(IntTo4Byte(sideLength)); - //height - bmp.AddRange(IntTo4Byte(sideLength)); + //width + bmp.AddRange(IntTo4Byte(sideLength)); + //height + bmp.AddRange(IntTo4Byte(sideLength)); - //header end - bmp.AddRange(new byte[] { 0x01, 0x00, 0x18, 0x00 }); + //header end + bmp.AddRange(new byte[] { 0x01, 0x00, 0x18, 0x00 }); - //draw qr code - for (var x = sideLength-1; x >= 0; x = x - pixelsPerModule) + //draw qr code + for (var x = sideLength - 1; x >= 0; x -= pixelsPerModule) + { + for (int pm = 0; pm < pixelsPerModule; pm++) { - for (int pm = 0; pm < pixelsPerModule; pm++) + for (var y = 0; y < sideLength; y += pixelsPerModule) { - for (var y = 0; y < sideLength; y = y + pixelsPerModule) + var module = + QrCodeData.ModuleMatrix[(x + pixelsPerModule) / pixelsPerModule - 1][(y + pixelsPerModule) / pixelsPerModule - 1]; + for (int i = 0; i < pixelsPerModule; i++) { - var module = - this.QrCodeData.ModuleMatrix[(x + pixelsPerModule)/pixelsPerModule - 1][(y + pixelsPerModule)/pixelsPerModule - 1]; - for (int i = 0; i < pixelsPerModule; i++) - { - bmp.AddRange(module ? moduleDark : moduleLight); - } + bmp.AddRange(module ? moduleDark : moduleLight); } - if (sideLength%4 != 0) + } + if (sideLength % 4 != 0) + { + for (int i = 0; i < sideLength % 4; i++) { - for (int i = 0; i < sideLength%4; i++) - { - bmp.Add(0x00); - } + bmp.Add(0x00); } } } + } - //finalize with terminator - bmp.AddRange(new byte[] { 0x00, 0x00 }); + //finalize with terminator + bmp.AddRange(new byte[] { 0x00, 0x00 }); - return bmp.ToArray(); - } + return bmp.ToArray(); + } - /// - /// Converts a hex color string to a byte array. - /// - /// The hex color string to convert. - /// Returns the color as a byte array. - private byte[] HexColorToByteArray(string colorString) - { - if (colorString.StartsWith("#")) - colorString = colorString.Substring(1); - byte[] byteColor = new byte[colorString.Length / 2]; - for (int i = 0; i < byteColor.Length; i++) - byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture); - return byteColor; - } + /// + /// Converts a hex color string to a byte array. + /// + /// The hex color string to convert. + /// Returns the color as a byte array. + private byte[] HexColorToByteArray(string colorString) + { + if (colorString.StartsWith("#")) + colorString = colorString.Substring(1); + byte[] byteColor = new byte[colorString.Length / 2]; + for (int i = 0; i < byteColor.Length; i++) + byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture); + return byteColor; + } - /// - /// Converts an integer to a 4-byte array. - /// - /// The integer to convert. - /// Returns the integer as a 4-byte array. - private byte[] IntTo4Byte(int inp) + /// + /// Converts an integer to a 4-byte array. + /// + /// The integer to convert. + /// Returns the integer as a 4-byte array. + private byte[] IntTo4Byte(int inp) + { + byte[] bytes = new byte[2]; + unchecked { - byte[] bytes = new byte[2]; - unchecked - { - bytes[1] = (byte)(inp >> 8); - bytes[0] = (byte)(inp); - } - return bytes; + bytes[1] = (byte)(inp >> 8); + bytes[0] = (byte)(inp); } + return bytes; } +} +/// +/// Provides helper functions for creating bitmap byte array QR codes. +/// +public static class BitmapByteQRCodeHelper +{ /// - /// Provides helper functions for creating bitmap byte array QR codes. + /// Creates a bitmap byte array QR code with a single function call. /// - public static class BitmapByteQRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Returns the QR code graphic as a bitmap byte array. + public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, + string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, + EciMode eciMode = EciMode.Default, int requestedVersion = -1) { - /// - /// Creates a bitmap byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Returns the QR code graphic as a bitmap byte array. - public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, - string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, - EciMode eciMode = EciMode.Default, int requestedVersion = -1) - { - using (var qrGenerator = new QRCodeGenerator()) - using ( - var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, - requestedVersion)) - using (var qrCode = new BitmapByteQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, + requestedVersion); + using var qrCode = new BitmapByteQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex); + } - /// - /// Creates a bitmap byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The level of error correction data. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a bitmap byte array. - public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size) - { - using (var qrGen = new QRCodeGenerator()) - using (var qrCode = qrGen.CreateQrCode(txt, eccLevel)) - using (var qrBmp = new BitmapByteQRCode(qrCode)) - return qrBmp.GetGraphic(size); + /// + /// Creates a bitmap byte array QR code with a single function call. + /// + /// The text or payload to be encoded inside the QR code. + /// The level of error correction data. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a bitmap byte array. + public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size) + { + using var qrGen = new QRCodeGenerator(); + using var qrCode = qrGen.CreateQrCode(txt, eccLevel); + using var qrBmp = new BitmapByteQRCode(qrCode); + return qrBmp.GetGraphic(size); - } } } diff --git a/QRCoder/Exceptions/DataTooLongException.cs b/QRCoder/Exceptions/DataTooLongException.cs index 04608257..5cc510b5 100644 --- a/QRCoder/Exceptions/DataTooLongException.cs +++ b/QRCoder/Exceptions/DataTooLongException.cs @@ -1,32 +1,32 @@ -using System; +using System; -namespace QRCoder.Exceptions +namespace QRCoder.Exceptions; + +/// +/// Exception thrown when the given payload exceeds the maximum size of the QR code standard. +/// +public class DataTooLongException : Exception { /// - /// Exception thrown when the given payload exceeds the maximum size of the QR code standard. + /// Initializes a new instance of the class with a specified error message. /// - public class DataTooLongException : Exception - { - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The error correction level of the QR code. - /// The encoding mode of the QR code. - /// The maximum size allowed for the given parameters in bytes. - public DataTooLongException(string eccLevel, string encodingMode, int maxSizeByte) : base( - $"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the chosen parameters (ECC level={eccLevel}, EncodingMode={encodingMode}) is {maxSizeByte} bytes." - ){} + /// The error correction level of the QR code. + /// The encoding mode of the QR code. + /// The maximum size allowed for the given parameters in bytes. + public DataTooLongException(string eccLevel, string encodingMode, int maxSizeByte) : base( + $"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the chosen parameters (ECC level={eccLevel}, EncodingMode={encodingMode}) is {maxSizeByte} bytes." + ) + { } - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The error correction level of the QR code. - /// The encoding mode of the QR code. - /// The fixed version of the QR code. - /// The maximum size allowed for the given parameters in bytes. - public DataTooLongException(string eccLevel, string encodingMode, int version, int maxSizeByte) : base( - $"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the chosen parameters (ECC level={eccLevel}, EncodingMode={encodingMode}, FixedVersion={version}) is {maxSizeByte} bytes." - ) - { } - } + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The error correction level of the QR code. + /// The encoding mode of the QR code. + /// The fixed version of the QR code. + /// The maximum size allowed for the given parameters in bytes. + public DataTooLongException(string eccLevel, string encodingMode, int version, int maxSizeByte) : base( + $"The given payload exceeds the maximum size of the QR code standard. The maximum size allowed for the chosen parameters (ECC level={eccLevel}, EncodingMode={encodingMode}, FixedVersion={version}) is {maxSizeByte} bytes." + ) + { } } diff --git a/QRCoder/Extensions/BitArrayExtensions.cs b/QRCoder/Extensions/BitArrayExtensions.cs index 4cddb522..4070de18 100644 --- a/QRCoder/Extensions/BitArrayExtensions.cs +++ b/QRCoder/Extensions/BitArrayExtensions.cs @@ -1,28 +1,27 @@ -using System.Collections; +using System.Collections; -namespace QRCoder +namespace QRCoder; + +/// +/// Helper methods for . +/// +internal static class BitArrayExtensions { /// - /// Helper methods for . + /// Copies a specified number of elements from one to another starting at the specified offsets. /// - internal static class BitArrayExtensions + /// The source from which elements will be copied. + /// The zero-based index in the source at which copying begins. + /// The destination to which elements will be copied. + /// The zero-based index in the destination at which storing begins. + /// The number of elements to copy. + /// The index in the destination immediately following the last copied element. + public static int CopyTo(this BitArray source, BitArray destination, int sourceOffset, int destinationOffset, int count) { - /// - /// Copies a specified number of elements from one to another starting at the specified offsets. - /// - /// The source from which elements will be copied. - /// The zero-based index in the source at which copying begins. - /// The destination to which elements will be copied. - /// The zero-based index in the destination at which storing begins. - /// The number of elements to copy. - /// The index in the destination immediately following the last copied element. - public static int CopyTo(this BitArray source, BitArray destination, int sourceOffset, int destinationOffset, int count) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - destination[destinationOffset + i] = source[sourceOffset + i]; - } - return destinationOffset + count; + destination[destinationOffset + i] = source[sourceOffset + i]; } + return destinationOffset + count; } } diff --git a/QRCoder/Extensions/StreamExtensions.cs b/QRCoder/Extensions/StreamExtensions.cs index 14796167..c260d327 100644 --- a/QRCoder/Extensions/StreamExtensions.cs +++ b/QRCoder/Extensions/StreamExtensions.cs @@ -1,19 +1,18 @@ -#if NET35 -namespace QRCoder +#if NET35 +namespace QRCoder; + +internal static class StreamExtensions { - internal static class StreamExtensions + /// + /// Copies a stream to another stream. + /// + public static void CopyTo(this System.IO.Stream input, System.IO.Stream output) { - /// - /// Copies a stream to another stream. - /// - public static void CopyTo(this System.IO.Stream input, System.IO.Stream output) + byte[] buffer = new byte[16 * 1024]; + int bytesRead; + while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) { - byte[] buffer = new byte[16 * 1024]; - int bytesRead; - while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) - { - output.Write(buffer, 0, bytesRead); - } + output.Write(buffer, 0, bytesRead); } } } diff --git a/QRCoder/Extensions/StringExtensions.cs b/QRCoder/Extensions/StringExtensions.cs index 51830f92..702c074e 100644 --- a/QRCoder/Extensions/StringExtensions.cs +++ b/QRCoder/Extensions/StringExtensions.cs @@ -1,31 +1,32 @@ using System.Diagnostics.CodeAnalysis; -namespace QRCoder +namespace QRCoder; + +internal static class StringExtensions { - internal static class StringExtensions + /// + /// Indicates whether the specified string is null, empty, or consists only of white-space characters. + /// + /// + /// if the is null, empty, or white space; otherwise, . + /// + public static bool IsNullOrWhiteSpace( + [NotNullWhen(false)] + this string? value) { - /// - /// Indicates whether the specified string is null, empty, or consists only of white-space characters. - /// - /// - /// if the is null, empty, or white space; otherwise, . - /// - public static bool IsNullOrWhiteSpace( - [NotNullWhen(false)] - this string? value) - { #if NET35 - if (value == null) return true; + if (value == null) + return true; - for (int i = 0; i < value.Length; i++) - { - if (!char.IsWhiteSpace(value[i])) return false; - } + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace(value[i])) + return false; + } - return true; + return true; #else - return string.IsNullOrWhiteSpace(value); + return string.IsNullOrWhiteSpace(value); #endif - } } -} \ No newline at end of file +} diff --git a/QRCoder/Extensions/StringValueAttribute.cs b/QRCoder/Extensions/StringValueAttribute.cs index 1bdcd46e..cf1f581a 100644 --- a/QRCoder/Extensions/StringValueAttribute.cs +++ b/QRCoder/Extensions/StringValueAttribute.cs @@ -1,59 +1,55 @@ -using System; -using System.Collections.Generic; +using System; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; -using System.Text; -namespace QRCoder.Extensions +namespace QRCoder.Extensions; + +/// +/// Used to represent a string value for a value in an enum +/// +[Obsolete("This attribute will be removed in a future version of QRCoder.")] +public class StringValueAttribute : Attribute { - /// - /// Used to represent a string value for a value in an enum - /// - [Obsolete("This attribute will be removed in a future version of QRCoder.")] - public class StringValueAttribute : Attribute - { - #region Properties + #region Properties - /// - /// Holds the alue in an enum - /// - public string StringValue { get; protected set; } + /// + /// Holds the alue in an enum + /// + public string StringValue { get; protected set; } - #endregion + #endregion - /// - /// Init a StringValue Attribute - /// - /// - public StringValueAttribute(string value) - { - this.StringValue = value; - } + /// + /// Init a StringValue Attribute + /// + /// + public StringValueAttribute(string value) + { + StringValue = value; } +} +/// +/// Enumeration extension methods. +/// +[Obsolete("This class will be removed in a future version of QRCoder.")] +public static class CustomExtensions +{ /// - /// Enumeration extension methods. + /// Will get the string value for a given enum's value /// - [Obsolete("This class will be removed in a future version of QRCoder.")] - public static class CustomExtensions - { - /// - /// Will get the string value for a given enum's value - /// #if NET6_0_OR_GREATER - [RequiresUnreferencedCode("This method uses reflection to examine the provided enum value.")] + [RequiresUnreferencedCode("This method uses reflection to examine the provided enum value.")] #endif - public static string? GetStringValue(this Enum value) - { + public static string? GetStringValue(this Enum value) + { #if NETSTANDARD1_3 - var fieldInfo = value.GetType().GetRuntimeField(value.ToString()); + var fieldInfo = value.GetType().GetRuntimeField(value.ToString()); #else - var fieldInfo = value.GetType().GetField(value.ToString())!; + var fieldInfo = value.GetType().GetField(value.ToString())!; #endif - var attr = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[]; - return attr!.Length > 0 ? attr[0].StringValue : null; - } + var attr = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[]; + return attr!.Length > 0 ? attr[0].StringValue : null; } } diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index 33e54614..a54efe31 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -1,144 +1,139 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System; using System.Globalization; +using System.Linq; using System.Text; using System.Text.RegularExpressions; -using System.Diagnostics.CodeAnalysis; #if NETSTANDARD1_3 using System.Reflection; #endif -namespace QRCoder +namespace QRCoder; + +/// +/// Provides utility methods for generating QR code payloads. +/// +public static partial class PayloadGenerator { /// - /// Provides utility methods for generating QR code payloads. + /// Validates the structure and checksum of an IBAN. /// - public static partial class PayloadGenerator + /// The IBAN to validate. + /// True if the IBAN is valid; otherwise, false. + private static bool IsValidIban(string iban) { - /// - /// Validates the structure and checksum of an IBAN. - /// - /// The IBAN to validate. - /// True if the IBAN is valid; otherwise, false. - private static bool IsValidIban(string iban) - { - //Clean IBAN - var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); + //Clean IBAN + var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); - //Check for general structure - var structurallyValid = Regex.IsMatch(ibanCleared, @"^[a-zA-Z]{2}[0-9]{2}([a-zA-Z0-9]?){16,30}$"); + //Check for general structure + var structurallyValid = Regex.IsMatch(ibanCleared, @"^[a-zA-Z]{2}[0-9]{2}([a-zA-Z0-9]?){16,30}$"); - //Check IBAN checksum - var checksumValid = false; - var sum = $"{ibanCleared.Substring(4)}{ibanCleared.Substring(0, 4)}".ToCharArray().Aggregate("", (current, c) => current + (char.IsLetter(c) ? (c - 55).ToString() : c.ToString())); - int m = 0; - for (int i = 0; i < (int)Math.Ceiling((sum.Length - 2) / 7d); i++){ - var offset = (i == 0 ? 0 : 2); - var start = i * 7 + offset; - var n = (i == 0 ? "" : m.ToString()) + sum.Substring(start, Math.Min(9 - offset, sum.Length - start)); - if (!int.TryParse(n, NumberStyles.Any, CultureInfo.InvariantCulture, out m)) - break; - m = m % 97; - } - checksumValid = m == 1; - return structurallyValid && checksumValid; - } - - /// - /// Validates the structure and checksum of a QR IBAN. - /// - /// The QR IBAN to validate. - /// True if the QR IBAN is valid; otherwise, false. - private static bool IsValidQRIban(string iban) + //Check IBAN checksum + var checksumValid = false; + var sum = $"{ibanCleared.Substring(4)}{ibanCleared.Substring(0, 4)}".ToCharArray().Aggregate("", (current, c) => current + (char.IsLetter(c) ? (c - 55).ToString() : c.ToString())); + int m = 0; + for (int i = 0; i < (int)Math.Ceiling((sum.Length - 2) / 7d); i++) { - var foundQrIid = false; - try - { - var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); - var possibleQrIid = Convert.ToInt32(ibanCleared.Substring(4, 5)); - foundQrIid = possibleQrIid >= 30000 && possibleQrIid <= 31999; - } catch { } - return IsValidIban(iban) && foundQrIid; + var offset = (i == 0 ? 0 : 2); + var start = i * 7 + offset; + var n = (i == 0 ? "" : m.ToString()) + sum.Substring(start, Math.Min(9 - offset, sum.Length - start)); + if (!int.TryParse(n, NumberStyles.Any, CultureInfo.InvariantCulture, out m)) + break; + m %= 97; } + checksumValid = m == 1; + return structurallyValid && checksumValid; + } - /// - /// Validates the structure of a BIC. - /// - /// The BIC to validate. - /// True if the BIC is valid; otherwise, false. - private static bool IsValidBic(string bic) + /// + /// Validates the structure and checksum of a QR IBAN. + /// + /// The QR IBAN to validate. + /// True if the QR IBAN is valid; otherwise, false. + private static bool IsValidQRIban(string iban) + { + var foundQrIid = false; + try { - return Regex.IsMatch(bic.Replace(" ", ""), @"^([a-zA-Z]{4}[a-zA-Z]{2}[a-zA-Z0-9]{2}([a-zA-Z0-9]{3})?)$"); + var ibanCleared = iban.ToUpper().Replace(" ", "").Replace("-", ""); + var possibleQrIid = Convert.ToInt32(ibanCleared.Substring(4, 5)); + foundQrIid = possibleQrIid >= 30000 && possibleQrIid <= 31999; } + catch { } + return IsValidIban(iban) && foundQrIid; + } + + /// + /// Validates the structure of a BIC. + /// + /// The BIC to validate. + /// True if the BIC is valid; otherwise, false. + private static bool IsValidBic(string bic) + => Regex.IsMatch(bic.Replace(" ", ""), @"^([a-zA-Z]{4}[a-zA-Z]{2}[a-zA-Z0-9]{2}([a-zA-Z0-9]{3})?)$"); - /// - /// Converts a string to a specified encoding. - /// - /// The string to convert. - /// The encoding to use. - /// The converted string. - private static string ConvertStringToEncoding(string message, string encoding) + /// + /// Converts a string to a specified encoding. + /// + /// The string to convert. + /// The encoding to use. + /// The converted string. + private static string ConvertStringToEncoding(string message, string encoding) + { + var iso = Encoding.GetEncoding(encoding); + var utf8 = Encoding.UTF8; + byte[] utfBytes = utf8.GetBytes(message); + byte[] isoBytes = Encoding.Convert(utf8, iso, utfBytes); + return iso.GetString(isoBytes); + } + + /// + /// Escapes forbidden characters in a string. + /// + /// The input string. + /// Indicates whether to use a simple escape mode. + /// The escaped string. + private static string EscapeInput(string inp, bool simple = false) + { + char[] forbiddenChars = { '\\', ';', ',', ':' }; + if (simple) { - Encoding iso = Encoding.GetEncoding(encoding); - Encoding utf8 = Encoding.UTF8; - byte[] utfBytes = utf8.GetBytes(message); - byte[] isoBytes = Encoding.Convert(utf8, iso, utfBytes); - return iso.GetString(isoBytes); + forbiddenChars = new char[1] { ':' }; } - - /// - /// Escapes forbidden characters in a string. - /// - /// The input string. - /// Indicates whether to use a simple escape mode. - /// The escaped string. - private static string EscapeInput(string inp, bool simple = false) + foreach (var c in forbiddenChars) { - char[] forbiddenChars = {'\\', ';', ',', ':'}; - if (simple) - { - forbiddenChars = new char[1] {':'}; - } - foreach (var c in forbiddenChars) - { - inp = inp.Replace(c.ToString(), "\\" + c); - } - return inp; + inp = inp.Replace(c.ToString(), "\\" + c); } + return inp; + } - /// - /// Validates a string using the Mod10 checksum algorithm. - /// - /// The string to validate. - /// True if the string is valid; otherwise, false. - public static bool ChecksumMod10(string digits) - { - if (string.IsNullOrEmpty(digits) || digits.Length < 2) - return false; - int[] mods = new int[] { 0, 9, 4, 6, 8, 2, 7, 1, 3, 5 }; - - int remainder = 0; - for (int i = 0; i < digits.Length - 1; i++) - { - var num = Convert.ToInt32(digits[i]) - 48; - remainder = mods[(num + remainder) % 10]; - } - var checksum = (10 - remainder) % 10; - return checksum == Convert.ToInt32(digits[digits.Length - 1]) - 48; - } + /// + /// Validates a string using the Mod10 checksum algorithm. + /// + /// The string to validate. + /// True if the string is valid; otherwise, false. + public static bool ChecksumMod10(string digits) + { + if (string.IsNullOrEmpty(digits) || digits.Length < 2) + return false; + int[] mods = new int[] { 0, 9, 4, 6, 8, 2, 7, 1, 3, 5 }; - /// - /// Checks if a string is in hexadecimal format. - /// - /// The input string. - /// True if the string is in hexadecimal format; otherwise, false. - private static bool isHexStyle(string inp) + int remainder = 0; + for (int i = 0; i < digits.Length - 1; i++) { - return (System.Text.RegularExpressions.Regex.IsMatch(inp, @"\A\b[0-9a-fA-F]+\b\Z") || System.Text.RegularExpressions.Regex.IsMatch(inp, @"\A\b(0[xX])?[0-9a-fA-F]+\b\Z")); + var num = Convert.ToInt32(digits[i]) - 48; + remainder = mods[(num + remainder) % 10]; } + var checksum = (10 - remainder) % 10; + return checksum == Convert.ToInt32(digits[digits.Length - 1]) - 48; } + + /// + /// Checks if a string is in hexadecimal format. + /// + /// The input string. + /// True if the string is in hexadecimal format; otherwise, false. + private static bool isHexStyle(string inp) + => System.Text.RegularExpressions.Regex.IsMatch(inp, @"\A\b[0-9a-fA-F]+\b\Z") || System.Text.RegularExpressions.Regex.IsMatch(inp, @"\A\b(0[xX])?[0-9a-fA-F]+\b\Z"); } diff --git a/QRCoder/PayloadGenerator/BezahlCode.cs b/QRCoder/PayloadGenerator/BezahlCode.cs index 486b1671..ffb2c184 100644 --- a/QRCoder/PayloadGenerator/BezahlCode.cs +++ b/QRCoder/PayloadGenerator/BezahlCode.cs @@ -1,589 +1,588 @@ -using System; +using System; using System.Text.RegularExpressions; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Represents a BezahlCode payload for QR codes. + /// + public class BezahlCode : Payload { - /// - /// Represents a BezahlCode payload for QR codes. - /// - public class BezahlCode : Payload - { - //BezahlCode specification: http://www.bezahlcode.de/wp-content/uploads/BezahlCode_TechDok.pdf + //BezahlCode specification: http://www.bezahlcode.de/wp-content/uploads/BezahlCode_TechDok.pdf - private readonly string? iban, bic, account, bnc, sepaReference, creditorId, mandateId, periodicTimeunit; - private readonly string name, reason; - private readonly decimal amount; - private readonly int postingKey, periodicTimeunitRotation; - private readonly Currency currency; - private readonly AuthorityType authority; - private readonly DateTime executionDate, dateOfSignature, periodicFirstExecutionDate, periodicLastExecutionDate; + private readonly string? _iban, _bic, _account, _bnc, _sepaReference, _creditorId, _mandateId, _periodicTimeunit; + private readonly string _name, _reason; + private readonly decimal _amount; + private readonly int _postingKey, _periodicTimeunitRotation; + private readonly Currency _currency; + private readonly AuthorityType _authority; + private readonly DateTime _executionDate, _dateOfSignature, _periodicFirstExecutionDate, _periodicLastExecutionDate; - /// - /// Initializes a new instance of the class for contact data. - /// - /// Type of the bank transfer - /// Name of the receiver (Empfänger) - /// Bank account (Kontonummer) - /// Bank institute (Bankleitzahl) - /// IBAN - /// BIC - /// Reason (Verwendungszweck) - public BezahlCode(AuthorityType authority, string name, string account = "", string bnc = "", string iban = "", string bic = "", string reason = "") : this(authority, name, account, bnc, iban, bic, 0, string.Empty, 0, null, null, string.Empty, string.Empty, null, reason, 0, string.Empty, Currency.EUR, null, 1) - { - } + /// + /// Initializes a new instance of the class for contact data. + /// + /// Type of the bank transfer + /// Name of the receiver (Empfänger) + /// Bank account (Kontonummer) + /// Bank institute (Bankleitzahl) + /// IBAN + /// BIC + /// Reason (Verwendungszweck) + public BezahlCode(AuthorityType authority, string name, string account = "", string bnc = "", string iban = "", string bic = "", string reason = "") : this(authority, name, account, bnc, iban, bic, 0, string.Empty, 0, null, null, string.Empty, string.Empty, null, reason, 0, string.Empty, Currency.EUR, null, 1) + { + } - /// - /// Initializes a new instance of the class for non-SEPA payments. - /// - /// Type of the bank transfer - /// Name of the receiver (Empfänger) - /// Bank account (Kontonummer) - /// Bank institute (Bankleitzahl) - /// Amount (Betrag) - /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) - /// Intervall for payment. This value is combined with 'periodicTimeunit' - /// Date of first periodic execution - /// Date of last periodic execution - /// Reason (Verwendungszweck) - /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) - /// Currency (Währung) - /// Execution date (Ausführungsdatum) - public BezahlCode(AuthorityType authority, string name, string account, string bnc, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string reason = "", int postingKey = 0, Currency currency = Currency.EUR, DateTime? executionDate = null) : this(authority, name, account, bnc, string.Empty, string.Empty, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, string.Empty, string.Empty, null, reason, postingKey, string.Empty, currency, executionDate, 2) - { - } + /// + /// Initializes a new instance of the class for non-SEPA payments. + /// + /// Type of the bank transfer + /// Name of the receiver (Empfänger) + /// Bank account (Kontonummer) + /// Bank institute (Bankleitzahl) + /// Amount (Betrag) + /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) + /// Intervall for payment. This value is combined with 'periodicTimeunit' + /// Date of first periodic execution + /// Date of last periodic execution + /// Reason (Verwendungszweck) + /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) + /// Currency (Währung) + /// Execution date (Ausführungsdatum) + public BezahlCode(AuthorityType authority, string name, string account, string bnc, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string reason = "", int postingKey = 0, Currency currency = Currency.EUR, DateTime? executionDate = null) : this(authority, name, account, bnc, string.Empty, string.Empty, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, string.Empty, string.Empty, null, reason, postingKey, string.Empty, currency, executionDate, 2) + { + } - /// - /// Initializes a new instance of the class for SEPA payments. - /// - /// Type of the bank transfer - /// Name of the receiver (Empfänger) - /// IBAN - /// BIC - /// Amount (Betrag) - /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) - /// Intervall for payment. This value is combined with 'periodicTimeunit' - /// Date of first periodic execution - /// Date of last periodic execution - /// Creditor id (Gläubiger ID) - /// Manadate id (Mandatsreferenz) - /// Signature date (Erteilungsdatum des Mandats) - /// Reason (Verwendungszweck) - /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) - /// SEPA reference (SEPA-Referenz) - /// Currency (Währung) - /// Execution date (Ausführungsdatum) - public BezahlCode(AuthorityType authority, string name, string iban, string bic, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string creditorId = "", string mandateId = "", DateTime? dateOfSignature = null, string reason = "", string sepaReference = "", Currency currency = Currency.EUR, DateTime? executionDate = null) : this(authority, name, string.Empty, string.Empty, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, creditorId, mandateId, dateOfSignature, reason, 0, sepaReference, currency, executionDate, 3) - { - } + /// + /// Initializes a new instance of the class for SEPA payments. + /// + /// Type of the bank transfer + /// Name of the receiver (Empfänger) + /// IBAN + /// BIC + /// Amount (Betrag) + /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) + /// Intervall for payment. This value is combined with 'periodicTimeunit' + /// Date of first periodic execution + /// Date of last periodic execution + /// Creditor id (Gläubiger ID) + /// Manadate id (Mandatsreferenz) + /// Signature date (Erteilungsdatum des Mandats) + /// Reason (Verwendungszweck) + /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) + /// SEPA reference (SEPA-Referenz) + /// Currency (Währung) + /// Execution date (Ausführungsdatum) + public BezahlCode(AuthorityType authority, string name, string iban, string bic, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string creditorId = "", string mandateId = "", DateTime? dateOfSignature = null, string reason = "", string sepaReference = "", Currency currency = Currency.EUR, DateTime? executionDate = null) : this(authority, name, string.Empty, string.Empty, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, creditorId, mandateId, dateOfSignature, reason, 0, sepaReference, currency, executionDate, 3) + { + } - /// - /// Generic constructor. Please use specific (non-SEPA or SEPA) constructor. - /// - /// Type of the bank transfer - /// Name of the receiver (Empfänger) - /// Bank account (Kontonummer) - /// Bank institute (Bankleitzahl) - /// IBAN - /// BIC - /// Amount (Betrag) - /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) - /// Intervall for payment. This value is combined with 'periodicTimeunit' - /// Date of first periodic execution - /// Date of last periodic execution - /// Creditor id (Gläubiger ID) - /// Manadate id (Mandatsreferenz) - /// Signature date (Erteilungsdatum des Mandats) - /// Reason (Verwendungszweck) - /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) - /// SEPA reference (SEPA-Referenz) - /// Currency (Währung) - /// Execution date (Ausführungsdatum) - /// Only used for internal state handdling - public BezahlCode(AuthorityType authority, string name, string account, string bnc, string iban, string bic, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string creditorId = "", string mandateId = "", DateTime? dateOfSignature = null, string reason = "", int postingKey = 0, string sepaReference = "", Currency currency = Currency.EUR, DateTime? executionDate = null, int internalMode = 0) + /// + /// Generic constructor. Please use specific (non-SEPA or SEPA) constructor. + /// + /// Type of the bank transfer + /// Name of the receiver (Empfänger) + /// Bank account (Kontonummer) + /// Bank institute (Bankleitzahl) + /// IBAN + /// BIC + /// Amount (Betrag) + /// Unit of intervall for payment ('M' = monthly, 'W' = weekly) + /// Intervall for payment. This value is combined with 'periodicTimeunit' + /// Date of first periodic execution + /// Date of last periodic execution + /// Creditor id (Gläubiger ID) + /// Manadate id (Mandatsreferenz) + /// Signature date (Erteilungsdatum des Mandats) + /// Reason (Verwendungszweck) + /// Transfer Key (Textschlüssel, z.B. Spendenzahlung = 69) + /// SEPA reference (SEPA-Referenz) + /// Currency (Währung) + /// Execution date (Ausführungsdatum) + /// Only used for internal state handdling + public BezahlCode(AuthorityType authority, string name, string account, string bnc, string iban, string bic, decimal amount, string periodicTimeunit = "", int periodicTimeunitRotation = 0, DateTime? periodicFirstExecutionDate = null, DateTime? periodicLastExecutionDate = null, string creditorId = "", string mandateId = "", DateTime? dateOfSignature = null, string reason = "", int postingKey = 0, string sepaReference = "", Currency currency = Currency.EUR, DateTime? executionDate = null, int internalMode = 0) + { + //Loaded via "contact-constructor" + if (internalMode == 1) { - //Loaded via "contact-constructor" - if (internalMode == 1) - { - if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) - throw new BezahlCodeException("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); - if (authority == AuthorityType.contact && (string.IsNullOrEmpty(account) || string.IsNullOrEmpty(bnc))) - throw new BezahlCodeException("When using authority type 'contact' the parameters 'account' and 'bnc' must be set."); + if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) + throw new BezahlCodeException("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); + if (authority == AuthorityType.contact && (string.IsNullOrEmpty(account) || string.IsNullOrEmpty(bnc))) + throw new BezahlCodeException("When using authority type 'contact' the parameters 'account' and 'bnc' must be set."); - if (authority != AuthorityType.contact_v2) - { - var oldFilled = (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(bnc)); - var newFilled = (!string.IsNullOrEmpty(iban) && !string.IsNullOrEmpty(bic)); - if ((!oldFilled && !newFilled) || (oldFilled && newFilled)) - throw new BezahlCodeException("When using authority type 'contact_v2' either the parameters 'account' and 'bnc' or the parameters 'iban' and 'bic' must be set. Leave the other parameter pair empty."); - } - } - else if (internalMode == 2) + if (authority != AuthorityType.contact_v2) { + var oldFilled = (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(bnc)); + var newFilled = (!string.IsNullOrEmpty(iban) && !string.IsNullOrEmpty(bic)); + if ((!oldFilled && !newFilled) || (oldFilled && newFilled)) + throw new BezahlCodeException("When using authority type 'contact_v2' either the parameters 'account' and 'bnc' or the parameters 'iban' and 'bic' must be set. Leave the other parameter pair empty."); + } + } + else if (internalMode == 2) + { #pragma warning disable CS0612 - if (authority != AuthorityType.periodicsinglepayment && authority != AuthorityType.singledirectdebit && authority != AuthorityType.singlepayment) - throw new BezahlCodeException("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); - if (authority == AuthorityType.periodicsinglepayment && (string.IsNullOrEmpty(periodicTimeunit) || periodicTimeunitRotation == 0)) - throw new BezahlCodeException("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); + if (authority != AuthorityType.periodicsinglepayment && authority != AuthorityType.singledirectdebit && authority != AuthorityType.singlepayment) + throw new BezahlCodeException("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); + if (authority == AuthorityType.periodicsinglepayment && (string.IsNullOrEmpty(periodicTimeunit) || periodicTimeunitRotation == 0)) + throw new BezahlCodeException("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); #pragma warning restore CS0612 - } - else if (internalMode == 3) - { - if (authority != AuthorityType.periodicsinglepaymentsepa && authority != AuthorityType.singledirectdebitsepa && authority != AuthorityType.singlepaymentsepa) - throw new BezahlCodeException("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); - if (authority == AuthorityType.periodicsinglepaymentsepa && (string.IsNullOrEmpty(periodicTimeunit) || periodicTimeunitRotation == 0)) - throw new BezahlCodeException("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); - } + } + else if (internalMode == 3) + { + if (authority != AuthorityType.periodicsinglepaymentsepa && authority != AuthorityType.singledirectdebitsepa && authority != AuthorityType.singlepaymentsepa) + throw new BezahlCodeException("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); + if (authority == AuthorityType.periodicsinglepaymentsepa && (string.IsNullOrEmpty(periodicTimeunit) || periodicTimeunitRotation == 0)) + throw new BezahlCodeException("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); + } - this.authority = authority; + _authority = authority; - if (name.Length > 70) - throw new BezahlCodeException("(Payee-)Name must be shorter than 71 chars."); - this.name = name; + if (name.Length > 70) + throw new BezahlCodeException("(Payee-)Name must be shorter than 71 chars."); + _name = name; - if (reason.Length > 27) - throw new BezahlCodeException("Reasons texts have to be shorter than 28 chars."); - this.reason = reason; + if (reason.Length > 27) + throw new BezahlCodeException("Reasons texts have to be shorter than 28 chars."); + _reason = reason; - var oldWayFilled = (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(bnc)); - var newWayFilled = (!string.IsNullOrEmpty(iban) && !string.IsNullOrEmpty(bic)); + var oldWayFilled = (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(bnc)); + var newWayFilled = (!string.IsNullOrEmpty(iban) && !string.IsNullOrEmpty(bic)); - //Non-SEPA payment types + //Non-SEPA payment types #pragma warning disable CS0612 - if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.singledirectdebit || authority == AuthorityType.singlepayment || authority == AuthorityType.contact || (authority == AuthorityType.contact_v2 && oldWayFilled)) - { + if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.singledirectdebit || authority == AuthorityType.singlepayment || authority == AuthorityType.contact || (authority == AuthorityType.contact_v2 && oldWayFilled)) + { #pragma warning restore CS0612 - if (!Regex.IsMatch(account.Replace(" ", ""), @"^[0-9]{1,9}$")) - throw new BezahlCodeException("The account entered isn't valid."); - this.account = account.Replace(" ", "").ToUpper(); - if(!Regex.IsMatch(bnc.Replace(" ", ""), @"^[0-9]{1,9}$")) - throw new BezahlCodeException("The bnc entered isn't valid."); - this.bnc = bnc.Replace(" ", "").ToUpper(); - - if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) - { - if (postingKey < 0 || postingKey >= 100) - throw new BezahlCodeException("PostingKey must be within 0 and 99."); - this.postingKey = postingKey; - } - } + if (!Regex.IsMatch(account.Replace(" ", ""), @"^[0-9]{1,9}$")) + throw new BezahlCodeException("The account entered isn't valid."); + _account = account.Replace(" ", "").ToUpper(); + if (!Regex.IsMatch(bnc.Replace(" ", ""), @"^[0-9]{1,9}$")) + throw new BezahlCodeException("The bnc entered isn't valid."); + _bnc = bnc.Replace(" ", "").ToUpper(); - //SEPA payment types - if (authority == AuthorityType.periodicsinglepaymentsepa || authority == AuthorityType.singledirectdebitsepa || authority == AuthorityType.singlepaymentsepa || (authority == AuthorityType.contact_v2 && newWayFilled)) + if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) { - if (!IsValidIban(iban)) - throw new BezahlCodeException("The IBAN entered isn't valid."); - this.iban = iban.Replace(" ", "").ToUpper(); - if (!IsValidBic(bic)) - throw new BezahlCodeException("The BIC entered isn't valid."); - this.bic = bic.Replace(" ", "").ToUpper(); - - if (authority != AuthorityType.contact_v2) - { - if (sepaReference.Length > 35) - throw new BezahlCodeException("SEPA reference texts have to be shorter than 36 chars."); - this.sepaReference = sepaReference; - - if (!string.IsNullOrEmpty(creditorId) && !Regex.IsMatch(creditorId.Replace(" ", ""), @"^[a-zA-Z]{2,2}[0-9]{2,2}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){3,3}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,28}$")) - throw new BezahlCodeException("The creditorId entered isn't valid."); - this.creditorId = creditorId; - if (!string.IsNullOrEmpty(mandateId) && !Regex.IsMatch(mandateId.Replace(" ", ""), @"^([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}$")) - throw new BezahlCodeException("The mandateId entered isn't valid."); - this.mandateId = mandateId; - if (dateOfSignature != null) - this.dateOfSignature = (DateTime)dateOfSignature; - } + if (postingKey < 0 || postingKey >= 100) + throw new BezahlCodeException("PostingKey must be within 0 and 99."); + _postingKey = postingKey; } + } - //Checks for all payment types - if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) + //SEPA payment types + if (authority == AuthorityType.periodicsinglepaymentsepa || authority == AuthorityType.singledirectdebitsepa || authority == AuthorityType.singlepaymentsepa || (authority == AuthorityType.contact_v2 && newWayFilled)) + { + if (!IsValidIban(iban)) + throw new BezahlCodeException("The IBAN entered isn't valid."); + _iban = iban.Replace(" ", "").ToUpper(); + if (!IsValidBic(bic)) + throw new BezahlCodeException("The BIC entered isn't valid."); + _bic = bic.Replace(" ", "").ToUpper(); + + if (authority != AuthorityType.contact_v2) { - if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",", ".").Split('.')[1].TrimEnd('0').Length > 2) - throw new BezahlCodeException("Amount must have less than 3 digits after decimal point."); - if (amount < 0.01m || amount > 999999999.99m) - throw new BezahlCodeException("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); - this.amount = amount; + if (sepaReference.Length > 35) + throw new BezahlCodeException("SEPA reference texts have to be shorter than 36 chars."); + _sepaReference = sepaReference; + + if (!string.IsNullOrEmpty(creditorId) && !Regex.IsMatch(creditorId.Replace(" ", ""), @"^[a-zA-Z]{2,2}[0-9]{2,2}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){3,3}([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,28}$")) + throw new BezahlCodeException("The creditorId entered isn't valid."); + _creditorId = creditorId; + if (!string.IsNullOrEmpty(mandateId) && !Regex.IsMatch(mandateId.Replace(" ", ""), @"^([A-Za-z0-9]|[\+|\?|/|\-|:|\(|\)|\.|,|']){1,35}$")) + throw new BezahlCodeException("The mandateId entered isn't valid."); + _mandateId = mandateId; + if (dateOfSignature != null) + _dateOfSignature = (DateTime)dateOfSignature; + } + } - this.currency = currency; + //Checks for all payment types + if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) + { + if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",", ".").Split('.')[1].TrimEnd('0').Length > 2) + throw new BezahlCodeException("Amount must have less than 3 digits after decimal point."); + if (amount < 0.01m || amount > 999999999.99m) + throw new BezahlCodeException("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); + _amount = amount; - if (executionDate == null) - this.executionDate = DateTime.Now; - else - { - if (DateTime.Today.Ticks > executionDate.Value.Ticks) - throw new BezahlCodeException("Execution date must be today or in future."); - this.executionDate = (DateTime)executionDate; - } + _currency = currency; + + if (executionDate == null) + _executionDate = DateTime.Now; + else + { + if (DateTime.Today.Ticks > executionDate.Value.Ticks) + throw new BezahlCodeException("Execution date must be today or in future."); + _executionDate = (DateTime)executionDate; + } #pragma warning disable CS0612 - if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.periodicsinglepaymentsepa) + if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.periodicsinglepaymentsepa) #pragma warning restore CS0612 - { - if (periodicTimeunit.ToUpper() != "M" && periodicTimeunit.ToUpper() != "W") - throw new BezahlCodeException("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); - this.periodicTimeunit = periodicTimeunit; - if (periodicTimeunitRotation < 1 || periodicTimeunitRotation > 52) - throw new BezahlCodeException("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); - this.periodicTimeunitRotation = periodicTimeunitRotation; - if (periodicFirstExecutionDate != null) - this.periodicFirstExecutionDate = (DateTime)periodicFirstExecutionDate; - if (periodicLastExecutionDate != null) - this.periodicLastExecutionDate = (DateTime)periodicLastExecutionDate; - } - + { + if (periodicTimeunit.ToUpper() != "M" && periodicTimeunit.ToUpper() != "W") + throw new BezahlCodeException("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); + _periodicTimeunit = periodicTimeunit; + if (periodicTimeunitRotation < 1 || periodicTimeunitRotation > 52) + throw new BezahlCodeException("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); + _periodicTimeunitRotation = periodicTimeunitRotation; + if (periodicFirstExecutionDate != null) + _periodicFirstExecutionDate = (DateTime)periodicFirstExecutionDate; + if (periodicLastExecutionDate != null) + _periodicLastExecutionDate = (DateTime)periodicLastExecutionDate; } + } - } - /// - public override string ToString() - { - var bezahlCodePayload = $"bank://{authority}?"; + } - bezahlCodePayload += $"name={Uri.EscapeDataString(name)}&"; + /// + public override string ToString() + { + var bezahlCodePayload = $"bank://{_authority}?"; - if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) - { - //Handle what is same for all payments + bezahlCodePayload += $"name={Uri.EscapeDataString(_name)}&"; + + if (_authority != AuthorityType.contact && _authority != AuthorityType.contact_v2) + { + //Handle what is same for all payments #pragma warning disable CS0612 - if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.singledirectdebit || authority == AuthorityType.singlepayment) + if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.singledirectdebit || _authority == AuthorityType.singlepayment) #pragma warning restore CS0612 + { + bezahlCodePayload += $"account={_account}&"; + bezahlCodePayload += $"bnc={_bnc}&"; + if (_postingKey > 0) + bezahlCodePayload += $"postingkey={_postingKey}&"; + } + else + { + bezahlCodePayload += $"iban={_iban}&"; + bezahlCodePayload += $"bic={_bic}&"; + + if (!string.IsNullOrEmpty(_sepaReference)) + bezahlCodePayload += $"separeference={Uri.EscapeDataString(_sepaReference)}&"; + + if (_authority == AuthorityType.singledirectdebitsepa) { - bezahlCodePayload += $"account={account}&"; - bezahlCodePayload += $"bnc={bnc}&"; - if (postingKey > 0) - bezahlCodePayload += $"postingkey={postingKey}&"; - } - else - { - bezahlCodePayload += $"iban={iban}&"; - bezahlCodePayload += $"bic={bic}&"; - - if (!string.IsNullOrEmpty(sepaReference)) - bezahlCodePayload += $"separeference={ Uri.EscapeDataString(sepaReference)}&"; - - if (authority == AuthorityType.singledirectdebitsepa) - { - if (!string.IsNullOrEmpty(creditorId)) - bezahlCodePayload += $"creditorid={ Uri.EscapeDataString(creditorId)}&"; - if (!string.IsNullOrEmpty(mandateId)) - bezahlCodePayload += $"mandateid={ Uri.EscapeDataString(mandateId)}&"; - if (dateOfSignature != DateTime.MinValue) - bezahlCodePayload += $"dateofsignature={dateOfSignature.ToString("ddMMyyyy")}&"; - } + if (!string.IsNullOrEmpty(_creditorId)) + bezahlCodePayload += $"creditorid={Uri.EscapeDataString(_creditorId)}&"; + if (!string.IsNullOrEmpty(_mandateId)) + bezahlCodePayload += $"mandateid={Uri.EscapeDataString(_mandateId)}&"; + if (_dateOfSignature != DateTime.MinValue) + bezahlCodePayload += $"dateofsignature={_dateOfSignature.ToString("ddMMyyyy")}&"; } - bezahlCodePayload += $"amount={amount:0.00}&".Replace(".", ","); + } + bezahlCodePayload += $"amount={_amount:0.00}&".Replace(".", ","); - if (!string.IsNullOrEmpty(reason)) - bezahlCodePayload += $"reason={ Uri.EscapeDataString(reason)}&"; - bezahlCodePayload += $"currency={currency}&"; - bezahlCodePayload += $"executiondate={executionDate.ToString("ddMMyyyy")}&"; + if (!string.IsNullOrEmpty(_reason)) + bezahlCodePayload += $"reason={Uri.EscapeDataString(_reason)}&"; + bezahlCodePayload += $"currency={_currency}&"; + bezahlCodePayload += $"executiondate={_executionDate.ToString("ddMMyyyy")}&"; #pragma warning disable CS0612 - if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.periodicsinglepaymentsepa) - { - bezahlCodePayload += $"periodictimeunit={periodicTimeunit}&"; - bezahlCodePayload += $"periodictimeunitrotation={periodicTimeunitRotation}&"; - if (periodicFirstExecutionDate != DateTime.MinValue) - bezahlCodePayload += $"periodicfirstexecutiondate={periodicFirstExecutionDate.ToString("ddMMyyyy")}&"; - if (periodicLastExecutionDate != DateTime.MinValue) - bezahlCodePayload += $"periodiclastexecutiondate={periodicLastExecutionDate.ToString("ddMMyyyy")}&"; - } + if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.periodicsinglepaymentsepa) + { + bezahlCodePayload += $"periodictimeunit={_periodicTimeunit}&"; + bezahlCodePayload += $"periodictimeunitrotation={_periodicTimeunitRotation}&"; + if (_periodicFirstExecutionDate != DateTime.MinValue) + bezahlCodePayload += $"periodicfirstexecutiondate={_periodicFirstExecutionDate.ToString("ddMMyyyy")}&"; + if (_periodicLastExecutionDate != DateTime.MinValue) + bezahlCodePayload += $"periodiclastexecutiondate={_periodicLastExecutionDate.ToString("ddMMyyyy")}&"; + } #pragma warning restore CS0612 + } + else + { + //Handle what is same for all contacts + if (_authority == AuthorityType.contact) + { + bezahlCodePayload += $"account={_account}&"; + bezahlCodePayload += $"bnc={_bnc}&"; } - else + else if (_authority == AuthorityType.contact_v2) { - //Handle what is same for all contacts - if (authority == AuthorityType.contact) + if (!string.IsNullOrEmpty(_account) && !string.IsNullOrEmpty(_bnc)) { - bezahlCodePayload += $"account={account}&"; - bezahlCodePayload += $"bnc={bnc}&"; + bezahlCodePayload += $"account={_account}&"; + bezahlCodePayload += $"bnc={_bnc}&"; } - else if (authority == AuthorityType.contact_v2) + else { - if (!string.IsNullOrEmpty(account) && !string.IsNullOrEmpty(bnc)) - { - bezahlCodePayload += $"account={account}&"; - bezahlCodePayload += $"bnc={bnc}&"; - } - else - { - bezahlCodePayload += $"iban={iban}&"; - bezahlCodePayload += $"bic={bic}&"; - } + bezahlCodePayload += $"iban={_iban}&"; + bezahlCodePayload += $"bic={_bic}&"; } - - if (!string.IsNullOrEmpty(reason)) - bezahlCodePayload += $"reason={ Uri.EscapeDataString(reason)}&"; } - return bezahlCodePayload.Trim('&'); + if (!string.IsNullOrEmpty(_reason)) + bezahlCodePayload += $"reason={Uri.EscapeDataString(_reason)}&"; } + return bezahlCodePayload.Trim('&'); + } + + /// + /// ISO 4217 currency codes + /// + public enum Currency + { + AED = 784, + AFN = 971, + ALL = 008, + AMD = 051, + ANG = 532, + AOA = 973, + ARS = 032, + AUD = 036, + AWG = 533, + AZN = 944, + BAM = 977, + BBD = 052, + BDT = 050, + BGN = 975, + BHD = 048, + BIF = 108, + BMD = 060, + BND = 096, + BOB = 068, + BOV = 984, + BRL = 986, + BSD = 044, + BTN = 064, + BWP = 072, + BYR = 974, + BZD = 084, + CAD = 124, + CDF = 976, + CHE = 947, + CHF = 756, + CHW = 948, + CLF = 990, + CLP = 152, + CNY = 156, + COP = 170, + COU = 970, + CRC = 188, + CUC = 931, + CUP = 192, + CVE = 132, + CZK = 203, + DJF = 262, + DKK = 208, + DOP = 214, + DZD = 012, + EGP = 818, + ERN = 232, + ETB = 230, + EUR = 978, + FJD = 242, + FKP = 238, + GBP = 826, + GEL = 981, + GHS = 936, + GIP = 292, + GMD = 270, + GNF = 324, + GTQ = 320, + GYD = 328, + HKD = 344, + HNL = 340, + HRK = 191, + HTG = 332, + HUF = 348, + IDR = 360, + ILS = 376, + INR = 356, + IQD = 368, + IRR = 364, + ISK = 352, + JMD = 388, + JOD = 400, + JPY = 392, + KES = 404, + KGS = 417, + KHR = 116, + KMF = 174, + KPW = 408, + KRW = 410, + KWD = 414, + KYD = 136, + KZT = 398, + LAK = 418, + LBP = 422, + LKR = 144, + LRD = 430, + LSL = 426, + LYD = 434, + MAD = 504, + MDL = 498, + MGA = 969, + MKD = 807, + MMK = 104, + MNT = 496, + MOP = 446, + MRO = 478, + MUR = 480, + MVR = 462, + MWK = 454, + MXN = 484, + MXV = 979, + MYR = 458, + MZN = 943, + NAD = 516, + NGN = 566, + NIO = 558, + NOK = 578, + NPR = 524, + NZD = 554, + OMR = 512, + PAB = 590, + PEN = 604, + PGK = 598, + PHP = 608, + PKR = 586, + PLN = 985, + PYG = 600, + QAR = 634, + RON = 946, + RSD = 941, + RUB = 643, + RWF = 646, + SAR = 682, + SBD = 090, + SCR = 690, + SDG = 938, + SEK = 752, + SGD = 702, + SHP = 654, + SLL = 694, + SOS = 706, + SRD = 968, + SSP = 728, + STD = 678, + SVC = 222, + SYP = 760, + SZL = 748, + THB = 764, + TJS = 972, + TMT = 934, + TND = 788, + TOP = 776, + TRY = 949, + TTD = 780, + TWD = 901, + TZS = 834, + UAH = 980, + UGX = 800, + USD = 840, + USN = 997, + UYI = 940, + UYU = 858, + UZS = 860, + VEF = 937, + VND = 704, + VUV = 548, + WST = 882, + XAF = 950, + XAG = 961, + XAU = 959, + XBA = 955, + XBB = 956, + XBC = 957, + XBD = 958, + XCD = 951, + XDR = 960, + XOF = 952, + XPD = 964, + XPF = 953, + XPT = 962, + XSU = 994, + XTS = 963, + XUA = 965, + XXX = 999, + YER = 886, + ZAR = 710, + ZMW = 967, + ZWL = 932 + } + + + /// + /// Operation modes of the BezahlCode + /// + public enum AuthorityType + { + /// + /// Single payment (Überweisung) + /// + [Obsolete] + singlepayment, + /// + /// Single SEPA payment (SEPA-Überweisung) + /// + singlepaymentsepa, /// - /// ISO 4217 currency codes + /// Single debit (Lastschrift) /// - public enum Currency + [Obsolete] + singledirectdebit, + /// + /// Single SEPA debit (SEPA-Lastschrift) + /// + singledirectdebitsepa, + /// + /// Periodic payment (Dauerauftrag) + /// + [Obsolete] + periodicsinglepayment, + /// + /// Periodic SEPA payment (SEPA-Dauerauftrag) + /// + periodicsinglepaymentsepa, + /// + /// Contact data + /// + contact, + /// + /// Contact data V2 + /// + contact_v2 + } + + /// + /// Exception class for BezahlCode errors. + /// + public class BezahlCodeException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public BezahlCodeException() { - AED = 784, - AFN = 971, - ALL = 008, - AMD = 051, - ANG = 532, - AOA = 973, - ARS = 032, - AUD = 036, - AWG = 533, - AZN = 944, - BAM = 977, - BBD = 052, - BDT = 050, - BGN = 975, - BHD = 048, - BIF = 108, - BMD = 060, - BND = 096, - BOB = 068, - BOV = 984, - BRL = 986, - BSD = 044, - BTN = 064, - BWP = 072, - BYR = 974, - BZD = 084, - CAD = 124, - CDF = 976, - CHE = 947, - CHF = 756, - CHW = 948, - CLF = 990, - CLP = 152, - CNY = 156, - COP = 170, - COU = 970, - CRC = 188, - CUC = 931, - CUP = 192, - CVE = 132, - CZK = 203, - DJF = 262, - DKK = 208, - DOP = 214, - DZD = 012, - EGP = 818, - ERN = 232, - ETB = 230, - EUR = 978, - FJD = 242, - FKP = 238, - GBP = 826, - GEL = 981, - GHS = 936, - GIP = 292, - GMD = 270, - GNF = 324, - GTQ = 320, - GYD = 328, - HKD = 344, - HNL = 340, - HRK = 191, - HTG = 332, - HUF = 348, - IDR = 360, - ILS = 376, - INR = 356, - IQD = 368, - IRR = 364, - ISK = 352, - JMD = 388, - JOD = 400, - JPY = 392, - KES = 404, - KGS = 417, - KHR = 116, - KMF = 174, - KPW = 408, - KRW = 410, - KWD = 414, - KYD = 136, - KZT = 398, - LAK = 418, - LBP = 422, - LKR = 144, - LRD = 430, - LSL = 426, - LYD = 434, - MAD = 504, - MDL = 498, - MGA = 969, - MKD = 807, - MMK = 104, - MNT = 496, - MOP = 446, - MRO = 478, - MUR = 480, - MVR = 462, - MWK = 454, - MXN = 484, - MXV = 979, - MYR = 458, - MZN = 943, - NAD = 516, - NGN = 566, - NIO = 558, - NOK = 578, - NPR = 524, - NZD = 554, - OMR = 512, - PAB = 590, - PEN = 604, - PGK = 598, - PHP = 608, - PKR = 586, - PLN = 985, - PYG = 600, - QAR = 634, - RON = 946, - RSD = 941, - RUB = 643, - RWF = 646, - SAR = 682, - SBD = 090, - SCR = 690, - SDG = 938, - SEK = 752, - SGD = 702, - SHP = 654, - SLL = 694, - SOS = 706, - SRD = 968, - SSP = 728, - STD = 678, - SVC = 222, - SYP = 760, - SZL = 748, - THB = 764, - TJS = 972, - TMT = 934, - TND = 788, - TOP = 776, - TRY = 949, - TTD = 780, - TWD = 901, - TZS = 834, - UAH = 980, - UGX = 800, - USD = 840, - USN = 997, - UYI = 940, - UYU = 858, - UZS = 860, - VEF = 937, - VND = 704, - VUV = 548, - WST = 882, - XAF = 950, - XAG = 961, - XAU = 959, - XBA = 955, - XBB = 956, - XBC = 957, - XBD = 958, - XCD = 951, - XDR = 960, - XOF = 952, - XPD = 964, - XPF = 953, - XPT = 962, - XSU = 994, - XTS = 963, - XUA = 965, - XXX = 999, - YER = 886, - ZAR = 710, - ZMW = 967, - ZWL = 932 } - /// - /// Operation modes of the BezahlCode + /// Initializes a new instance of the class with a specified error message. /// - public enum AuthorityType + /// The message that describes the error. + public BezahlCodeException(string message) + : base(message) { - /// - /// Single payment (Überweisung) - /// - [Obsolete] - singlepayment, - /// - /// Single SEPA payment (SEPA-Überweisung) - /// - singlepaymentsepa, - /// - /// Single debit (Lastschrift) - /// - [Obsolete] - singledirectdebit, - /// - /// Single SEPA debit (SEPA-Lastschrift) - /// - singledirectdebitsepa, - /// - /// Periodic payment (Dauerauftrag) - /// - [Obsolete] - periodicsinglepayment, - /// - /// Periodic SEPA payment (SEPA-Dauerauftrag) - /// - periodicsinglepaymentsepa, - /// - /// Contact data - /// - contact, - /// - /// Contact data V2 - /// - contact_v2 } /// - /// Exception class for BezahlCode errors. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class BezahlCodeException : Exception + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public BezahlCodeException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public BezahlCodeException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public BezahlCodeException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The message that describes the error. - /// The exception that is the cause of the current exception. - public BezahlCodeException(string message, Exception inner) - : base(message, inner) - { - } } } } diff --git a/QRCoder/PayloadGenerator/BitcoinAddress.cs b/QRCoder/PayloadGenerator/BitcoinAddress.cs index 74f701a1..62a9b180 100644 --- a/QRCoder/PayloadGenerator/BitcoinAddress.cs +++ b/QRCoder/PayloadGenerator/BitcoinAddress.cs @@ -1,22 +1,21 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for Bitcoin payment addresses. + /// + public class BitcoinAddress : BitcoinLikeCryptoCurrencyAddress { /// - /// Generates a payload for Bitcoin payment addresses. + /// Initializes a new instance of the class. + /// Generates a Bitcoin payment payload. QR Codes with this payload can open a payment app. /// - public class BitcoinAddress : BitcoinLikeCryptoCurrencyAddress - { - /// - /// Initializes a new instance of the class. - /// Generates a Bitcoin payment payload. QR Codes with this payload can open a payment app. - /// - /// The Bitcoin address of the payment receiver. - /// The amount of Bitcoin to transfer. - /// A reference label. - /// A reference text or message. - public BitcoinAddress(string address, double? amount, string? label = null, string? message = null) - : base(BitcoinLikeCryptoCurrencyType.Bitcoin, address, amount, label, message) { } - } + /// The Bitcoin address of the payment receiver. + /// The amount of Bitcoin to transfer. + /// A reference label. + /// A reference text or message. + public BitcoinAddress(string address, double? amount, string? label = null, string? message = null) + : base(BitcoinLikeCryptoCurrencyType.Bitcoin, address, amount, label, message) { } } } diff --git a/QRCoder/PayloadGenerator/BitcoinCashAddress.cs b/QRCoder/PayloadGenerator/BitcoinCashAddress.cs index e2411cfb..0f89b11f 100644 --- a/QRCoder/PayloadGenerator/BitcoinCashAddress.cs +++ b/QRCoder/PayloadGenerator/BitcoinCashAddress.cs @@ -1,22 +1,21 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for Bitcoin Cash payment addresses. + /// + public class BitcoinCashAddress : BitcoinLikeCryptoCurrencyAddress { /// - /// Generates a payload for Bitcoin Cash payment addresses. + /// Initializes a new instance of the class. + /// Generates a Bitcoin Cash payment payload. QR Codes with this payload can open a payment app. /// - public class BitcoinCashAddress : BitcoinLikeCryptoCurrencyAddress - { - /// - /// Initializes a new instance of the class. - /// Generates a Bitcoin Cash payment payload. QR Codes with this payload can open a payment app. - /// - /// The Bitcoin Cash address of the payment receiver. - /// The amount of Bitcoin Cash to transfer. - /// A reference label. - /// A reference text or message. - public BitcoinCashAddress(string address, double? amount, string? label = null, string? message = null) - : base(BitcoinLikeCryptoCurrencyType.BitcoinCash, address, amount, label, message) { } - } + /// The Bitcoin Cash address of the payment receiver. + /// The amount of Bitcoin Cash to transfer. + /// A reference label. + /// A reference text or message. + public BitcoinCashAddress(string address, double? amount, string? label = null, string? message = null) + : base(BitcoinLikeCryptoCurrencyType.BitcoinCash, address, amount, label, message) { } } } diff --git a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs index 62853670..81889334 100644 --- a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs +++ b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs @@ -1,94 +1,93 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Globalization; +using System.Linq; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for Bitcoin-like cryptocurrency payment addresses. + /// + public class BitcoinLikeCryptoCurrencyAddress : Payload { + private readonly BitcoinLikeCryptoCurrencyType _currencyType; + private readonly string _address; + private readonly string? _label, _message; + private readonly double? _amount; + /// - /// Generates a payload for Bitcoin-like cryptocurrency payment addresses. + /// Initializes a new instance of the class. + /// Generates a Bitcoin-like cryptocurrency payment payload. QR Codes with this payload can open a payment app. /// - public class BitcoinLikeCryptoCurrencyAddress : Payload + /// The type of Bitcoin-like cryptocurrency. + /// The cryptocurrency address of the payment receiver. + /// The amount of coins to transfer. + /// A reference label. + /// A reference text or message. + public BitcoinLikeCryptoCurrencyAddress(BitcoinLikeCryptoCurrencyType currencyType, string address, double? amount, string? label = null, string? message = null) { - private readonly BitcoinLikeCryptoCurrencyType currencyType; - private readonly string address; - private readonly string? label, message; - private readonly double? amount; + _currencyType = currencyType; + _address = address; - /// - /// Initializes a new instance of the class. - /// Generates a Bitcoin-like cryptocurrency payment payload. QR Codes with this payload can open a payment app. - /// - /// The type of Bitcoin-like cryptocurrency. - /// The cryptocurrency address of the payment receiver. - /// The amount of coins to transfer. - /// A reference label. - /// A reference text or message. - public BitcoinLikeCryptoCurrencyAddress(BitcoinLikeCryptoCurrencyType currencyType, string address, double? amount, string? label = null, string? message = null) + if (!string.IsNullOrEmpty(label)) { - this.currencyType = currencyType; - this.address = address; - - if (!string.IsNullOrEmpty(label)) - { - this.label = Uri.EscapeDataString(label); - } - - if (!string.IsNullOrEmpty(message)) - { - this.message = Uri.EscapeDataString(message); - } - - this.amount = amount; + _label = Uri.EscapeDataString(label); } - /// - /// Returns a string representation of the Bitcoin-like cryptocurrency address payload. - /// - /// A string representation of the cryptocurrency address payload. - public override string ToString() + if (!string.IsNullOrEmpty(message)) { - string? query = null; + _message = Uri.EscapeDataString(message); + } - var queryValues = new KeyValuePair[]{ - new KeyValuePair(nameof(label), label), - new KeyValuePair(nameof(message), message), - new KeyValuePair(nameof(amount), amount.HasValue ? amount.Value.ToString("#.########", CultureInfo.InvariantCulture) : null) - }; + _amount = amount; + } - if (queryValues.Any(keyPair => !string.IsNullOrEmpty(keyPair.Value))) - { - query = "?" + string.Join("&", queryValues - .Where(keyPair => !string.IsNullOrEmpty(keyPair.Value)) - .Select(keyPair => $"{keyPair.Key}={keyPair.Value}") - .ToArray()); - } + /// + /// Returns a string representation of the Bitcoin-like cryptocurrency address payload. + /// + /// A string representation of the cryptocurrency address payload. + public override string ToString() + { + string? query = null; + + var queryValues = new KeyValuePair[]{ + new KeyValuePair("label", _label), + new KeyValuePair("message", _message), + new KeyValuePair("amount", _amount.HasValue ? _amount.Value.ToString("#.########", CultureInfo.InvariantCulture) : null) + }; - return $"{Enum.GetName(typeof(BitcoinLikeCryptoCurrencyType), currencyType)!.ToLower()}:{address}{query}"; + if (queryValues.Any(keyPair => !string.IsNullOrEmpty(keyPair.Value))) + { + query = "?" + string.Join("&", queryValues + .Where(keyPair => !string.IsNullOrEmpty(keyPair.Value)) + .Select(keyPair => $"{keyPair.Key}={keyPair.Value}") + .ToArray()); } + return $"{Enum.GetName(typeof(BitcoinLikeCryptoCurrencyType), _currencyType)!.ToLower()}:{_address}{query}"; + } + + /// + /// Enumerates Bitcoin-like cryptocurrencies. + /// + public enum BitcoinLikeCryptoCurrencyType + { /// - /// Enumerates Bitcoin-like cryptocurrencies. + /// Bitcoin. /// - public enum BitcoinLikeCryptoCurrencyType - { - /// - /// Bitcoin. - /// - Bitcoin, + Bitcoin, - /// - /// Bitcoin Cash. - /// - BitcoinCash, + /// + /// Bitcoin Cash. + /// + BitcoinCash, - /// - /// Litecoin. - /// - Litecoin - } + /// + /// Litecoin. + /// + Litecoin } } } diff --git a/QRCoder/PayloadGenerator/Bookmark.cs b/QRCoder/PayloadGenerator/Bookmark.cs index cc3c4413..7ca09977 100644 --- a/QRCoder/PayloadGenerator/Bookmark.cs +++ b/QRCoder/PayloadGenerator/Bookmark.cs @@ -1,33 +1,30 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a bookmark payload. When scanned by a QR code reader, this creates a browser bookmark. + /// + public class Bookmark : Payload { + private readonly string _url, _title; + /// - /// Generates a bookmark payload. When scanned by a QR code reader, this creates a browser bookmark. + /// Initializes a new instance of the class. /// - public class Bookmark : Payload + /// The URL of the bookmark. + /// The title of the bookmark. + public Bookmark(string url, string title) { - private readonly string url, title; - - /// - /// Initializes a new instance of the class. - /// - /// The URL of the bookmark. - /// The title of the bookmark. - public Bookmark(string url, string title) - { - this.url = EscapeInput(url); - this.title = EscapeInput(title); - } - - /// - /// Returns a string representation of the bookmark payload. - /// - /// A string representation of the bookmark payload in the MEBKM format. - public override string ToString() - { - return $"MEBKM:TITLE:{this.title};URL:{this.url};;"; - } + _url = EscapeInput(url); + _title = EscapeInput(title); } + + /// + /// Returns a string representation of the bookmark payload. + /// + /// A string representation of the bookmark payload in the MEBKM format. + public override string ToString() + => $"MEBKM:TITLE:{_title};URL:{_url};;"; } } diff --git a/QRCoder/PayloadGenerator/CalendarEvent.cs b/QRCoder/PayloadGenerator/CalendarEvent.cs index b6d22f9a..9e633a90 100644 --- a/QRCoder/PayloadGenerator/CalendarEvent.cs +++ b/QRCoder/PayloadGenerator/CalendarEvent.cs @@ -1,96 +1,95 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a calendar entry/event payload. + /// + public class CalendarEvent : Payload { + private readonly string _subject, _start, _end; + private readonly string? _description, _location; + private readonly EventEncoding _encoding; + /// - /// Generates a calendar entry/event payload. + /// Initializes a new instance of the class. /// - public class CalendarEvent : Payload + /// Subject/title of the calendar event. + /// Description of the event. + /// Location (latitude:longitude or address) of the event. + /// Start time (including UTC offset) of the event. + /// End time (including UTC offset) of the event. + /// Indicates if it is a full day event. + /// Type of encoding (universal or iCal). + public CalendarEvent(string subject, string? description, string? location, DateTimeOffset start, DateTimeOffset end, bool allDayEvent, EventEncoding encoding = EventEncoding.Universal) : this(subject, description, location, start.UtcDateTime, end.UtcDateTime, allDayEvent, encoding) { - private readonly string subject, start, end; - private readonly string? description, location; - private readonly EventEncoding encoding; - - /// - /// Initializes a new instance of the class. - /// - /// Subject/title of the calendar event. - /// Description of the event. - /// Location (latitude:longitude or address) of the event. - /// Start time (including UTC offset) of the event. - /// End time (including UTC offset) of the event. - /// Indicates if it is a full day event. - /// Type of encoding (universal or iCal). - public CalendarEvent(string subject, string? description, string? location, DateTimeOffset start, DateTimeOffset end, bool allDayEvent, EventEncoding encoding = EventEncoding.Universal) : this(subject, description, location, start.UtcDateTime, end.UtcDateTime, allDayEvent, encoding) - { - } + } - /// - /// Initializes a new instance of the class. - /// - /// Subject/title of the calendar event. - /// Description of the event. - /// Location (latitude:longitude or address) of the event. - /// Start time of the event. - /// End time of the event. - /// Indicates if it is a full day event. - /// Type of encoding (universal or iCal). - public CalendarEvent(string subject, string? description, string? location, DateTime start, DateTime end, bool allDayEvent, EventEncoding encoding = EventEncoding.Universal) + /// + /// Initializes a new instance of the class. + /// + /// Subject/title of the calendar event. + /// Description of the event. + /// Location (latitude:longitude or address) of the event. + /// Start time of the event. + /// End time of the event. + /// Indicates if it is a full day event. + /// Type of encoding (universal or iCal). + public CalendarEvent(string subject, string? description, string? location, DateTime start, DateTime end, bool allDayEvent, EventEncoding encoding = EventEncoding.Universal) + { + _subject = subject; + _description = description; + _location = location; + _encoding = encoding; + string dtFormatStart = "yyyyMMdd", dtFormatEnd = "yyyyMMdd"; + if (!allDayEvent) { - this.subject = subject; - this.description = description; - this.location = location; - this.encoding = encoding; - string dtFormatStart = "yyyyMMdd", dtFormatEnd = "yyyyMMdd"; - if (!allDayEvent) - { - dtFormatStart = dtFormatEnd = "yyyyMMddTHHmmss"; - if (start.Kind == DateTimeKind.Utc) - dtFormatStart = "yyyyMMddTHHmmssZ"; - if (end.Kind == DateTimeKind.Utc) - dtFormatEnd = "yyyyMMddTHHmmssZ"; - } - this.start = start.ToString(dtFormatStart); - this.end = end.ToString(dtFormatEnd); + dtFormatStart = dtFormatEnd = "yyyyMMddTHHmmss"; + if (start.Kind == DateTimeKind.Utc) + dtFormatStart = "yyyyMMddTHHmmssZ"; + if (end.Kind == DateTimeKind.Utc) + dtFormatEnd = "yyyyMMddTHHmmssZ"; } + _start = start.ToString(dtFormatStart); + _end = end.ToString(dtFormatEnd); + } - /// - /// Returns a string representation of the calendar event payload. - /// - /// A string representation of the calendar event in the VEVENT format. - public override string ToString() - { - var vEvent = $"BEGIN:VEVENT{Environment.NewLine}"; - vEvent += $"SUMMARY:{this.subject}{Environment.NewLine}"; - vEvent += !string.IsNullOrEmpty(this.description) ? $"DESCRIPTION:{this.description}{Environment.NewLine}" : ""; - vEvent += !string.IsNullOrEmpty(this.location) ? $"LOCATION:{this.location}{Environment.NewLine}" : ""; - vEvent += $"DTSTART:{this.start}{Environment.NewLine}"; - vEvent += $"DTEND:{this.end}{Environment.NewLine}"; - vEvent += "END:VEVENT"; + /// + /// Returns a string representation of the calendar event payload. + /// + /// A string representation of the calendar event in the VEVENT format. + public override string ToString() + { + var vEvent = $"BEGIN:VEVENT{Environment.NewLine}"; + vEvent += $"SUMMARY:{_subject}{Environment.NewLine}"; + vEvent += !string.IsNullOrEmpty(_description) ? $"DESCRIPTION:{_description}{Environment.NewLine}" : ""; + vEvent += !string.IsNullOrEmpty(_location) ? $"LOCATION:{_location}{Environment.NewLine}" : ""; + vEvent += $"DTSTART:{_start}{Environment.NewLine}"; + vEvent += $"DTEND:{_end}{Environment.NewLine}"; + vEvent += "END:VEVENT"; - if (this.encoding == EventEncoding.iCalComplete) - vEvent = $@"BEGIN:VCALENDAR{Environment.NewLine}VERSION:2.0{Environment.NewLine}{vEvent}{Environment.NewLine}END:VCALENDAR"; + if (_encoding == EventEncoding.iCalComplete) + vEvent = $@"BEGIN:VCALENDAR{Environment.NewLine}VERSION:2.0{Environment.NewLine}{vEvent}{Environment.NewLine}END:VCALENDAR"; - return vEvent; - } + return vEvent; + } + /// + /// Specifies the encoding type for the calendar event. + /// + public enum EventEncoding + { /// - /// Specifies the encoding type for the calendar event. + /// iCalendar complete encoding. /// - public enum EventEncoding - { - /// - /// iCalendar complete encoding. - /// - iCalComplete, + iCalComplete, - /// - /// Universal encoding. - /// - Universal - } + /// + /// Universal encoding. + /// + Universal } } } diff --git a/QRCoder/PayloadGenerator/ContactData.cs b/QRCoder/PayloadGenerator/ContactData.cs index d6fdfbdf..8c6c6df7 100644 --- a/QRCoder/PayloadGenerator/ContactData.cs +++ b/QRCoder/PayloadGenerator/ContactData.cs @@ -1,266 +1,265 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a vCard or meCard contact dataset. + /// + public class ContactData : Payload { + private readonly string _firstname; + private readonly string _lastname; + private readonly string? _nickname; + private readonly string? _org; + private readonly string? _orgTitle; + private readonly string? _phone; + private readonly string? _mobilePhone; + private readonly string? _workPhone; + private readonly string? _email; + private readonly DateTime? _birthday; + private readonly string? _website; + private readonly string? _street; + private readonly string? _houseNumber; + private readonly string? _city; + private readonly string? _zipCode; + private readonly string? _stateRegion; + private readonly string? _country; + private readonly string? _note; + private readonly ContactOutputType _outputType; + private readonly AddressOrder _addressOrder; + + /// - /// Generates a vCard or meCard contact dataset. + /// Initializes a new instance of the class. /// - public class ContactData : Payload + /// Payload output type. + /// The first name. + /// The last name. + /// The display name. + /// Normal phone number. + /// Mobile phone. + /// Office phone number. + /// E-Mail address. + /// Birthday. + /// Website / Homepage. + /// Street. + /// House number. + /// City. + /// State or Region. + /// Zip code. + /// Country. + /// The address order format to use. + /// Memo text / notes. + /// Organization/Company. + /// Organization/Company Title. + public ContactData(ContactOutputType outputType, string firstname, string lastname, string? nickname = null, string? phone = null, string? mobilePhone = null, string? workPhone = null, string? email = null, DateTime? birthday = null, string? website = null, string? street = null, string? houseNumber = null, string? city = null, string? zipCode = null, string? country = null, string? note = null, string? stateRegion = null, AddressOrder addressOrder = AddressOrder.Default, string? org = null, string? orgTitle = null) { - private readonly string firstname; - private readonly string lastname; - private readonly string? nickname; - private readonly string? org; - private readonly string? orgTitle; - private readonly string? phone; - private readonly string? mobilePhone; - private readonly string? workPhone; - private readonly string? email; - private readonly DateTime? birthday; - private readonly string? website; - private readonly string? street; - private readonly string? houseNumber; - private readonly string? city; - private readonly string? zipCode; - private readonly string? stateRegion; - private readonly string? country; - private readonly string? note; - private readonly ContactOutputType outputType; - private readonly AddressOrder addressOrder; - - - /// - /// Initializes a new instance of the class. - /// - /// Payload output type. - /// The first name. - /// The last name. - /// The display name. - /// Normal phone number. - /// Mobile phone. - /// Office phone number. - /// E-Mail address. - /// Birthday. - /// Website / Homepage. - /// Street. - /// House number. - /// City. - /// State or Region. - /// Zip code. - /// Country. - /// The address order format to use. - /// Memo text / notes. - /// Organization/Company. - /// Organization/Company Title. - public ContactData(ContactOutputType outputType, string firstname, string lastname, string? nickname = null, string? phone = null, string? mobilePhone = null, string? workPhone = null, string? email = null, DateTime? birthday = null, string? website = null, string? street = null, string? houseNumber = null, string? city = null, string? zipCode = null, string? country = null, string? note = null, string? stateRegion = null, AddressOrder addressOrder = AddressOrder.Default, string? org = null, string? orgTitle = null) - { - this.firstname = firstname; - this.lastname = lastname; - this.nickname = nickname; - this.org = org; - this.orgTitle = orgTitle; - this.phone = phone; - this.mobilePhone = mobilePhone; - this.workPhone = workPhone; - this.email = email; - this.birthday = birthday; - this.website = website; - this.street = street; - this.houseNumber = houseNumber; - this.city = city; - this.stateRegion = stateRegion; - this.zipCode = zipCode; - this.country = country; - this.addressOrder = addressOrder; - this.note = note; - this.outputType = outputType; - } + _firstname = firstname; + _lastname = lastname; + _nickname = nickname; + _org = org; + _orgTitle = orgTitle; + _phone = phone; + _mobilePhone = mobilePhone; + _workPhone = workPhone; + _email = email; + _birthday = birthday; + _website = website; + _street = street; + _houseNumber = houseNumber; + _city = city; + _stateRegion = stateRegion; + _zipCode = zipCode; + _country = country; + _addressOrder = addressOrder; + _note = note; + _outputType = outputType; + } - /// - /// Returns a string representation of the contact data payload. - /// - /// A string representation of the contact data in the specified format. - public override string ToString() + /// + /// Returns a string representation of the contact data payload. + /// + /// A string representation of the contact data in the specified format. + public override string ToString() + { + string payload = string.Empty; + if (_outputType == ContactOutputType.MeCard) { - string payload = string.Empty; - if (outputType == ContactOutputType.MeCard) + payload += "MECARD+\r\n"; + if (!string.IsNullOrEmpty(_firstname) && !string.IsNullOrEmpty(_lastname)) + payload += $"N:{_lastname}, {_firstname}\r\n"; + else if (!string.IsNullOrEmpty(_firstname) || !string.IsNullOrEmpty(_lastname)) + payload += $"N:{_firstname}{_lastname}\r\n"; + if (!string.IsNullOrEmpty(_org)) + payload += $"ORG:{_org}\r\n"; + if (!string.IsNullOrEmpty(_orgTitle)) + payload += $"TITLE:{_orgTitle}\r\n"; + if (!string.IsNullOrEmpty(_phone)) + payload += $"TEL:{_phone}\r\n"; + if (!string.IsNullOrEmpty(_mobilePhone)) + payload += $"TEL:{_mobilePhone}\r\n"; + if (!string.IsNullOrEmpty(_workPhone)) + payload += $"TEL:{_workPhone}\r\n"; + if (!string.IsNullOrEmpty(_email)) + payload += $"EMAIL:{_email}\r\n"; + if (!string.IsNullOrEmpty(_note)) + payload += $"NOTE:{_note}\r\n"; + if (_birthday != null) + payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd")}\r\n"; + string addressString = string.Empty; + if (_addressOrder == AddressOrder.Default) { - payload += "MECARD+\r\n"; - if (!string.IsNullOrEmpty(firstname) && !string.IsNullOrEmpty(lastname)) - payload += $"N:{lastname}, {firstname}\r\n"; - else if (!string.IsNullOrEmpty(firstname) || !string.IsNullOrEmpty(lastname)) - payload += $"N:{firstname}{lastname}\r\n"; - if (!string.IsNullOrEmpty(org)) - payload += $"ORG:{org}\r\n"; - if (!string.IsNullOrEmpty(orgTitle)) - payload += $"TITLE:{orgTitle}\r\n"; - if (!string.IsNullOrEmpty(phone)) - payload += $"TEL:{phone}\r\n"; - if (!string.IsNullOrEmpty(mobilePhone)) - payload += $"TEL:{mobilePhone}\r\n"; - if (!string.IsNullOrEmpty(workPhone)) - payload += $"TEL:{workPhone}\r\n"; - if (!string.IsNullOrEmpty(email)) - payload += $"EMAIL:{email}\r\n"; - if (!string.IsNullOrEmpty(note)) - payload += $"NOTE:{note}\r\n"; - if (birthday != null) - payload += $"BDAY:{((DateTime)birthday).ToString("yyyyMMdd")}\r\n"; - string addressString = string.Empty; - if(addressOrder == AddressOrder.Default) - { - addressString = $"ADR:,,{(!string.IsNullOrEmpty(street) ? street + " " : "")}{(!string.IsNullOrEmpty(houseNumber) ? houseNumber : "")},{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")},{(!string.IsNullOrEmpty(city) ? city : "")},{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")},{(!string.IsNullOrEmpty(country) ? country : "")}\r\n"; - } - else - { - addressString = $"ADR:,,{(!string.IsNullOrEmpty(houseNumber) ? houseNumber + " " : "")}{(!string.IsNullOrEmpty(street) ? street : "")},{(!string.IsNullOrEmpty(city) ? city : "")},{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")},{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")},{(!string.IsNullOrEmpty(country) ? country : "")}\r\n"; - } - payload += addressString; - if (!string.IsNullOrEmpty(website)) - payload += $"URL:{website}\r\n"; - if (!string.IsNullOrEmpty(nickname)) - payload += $"NICKNAME:{nickname}\r\n"; - payload = payload.Trim(new char[] { '\r', '\n' }); + addressString = $"ADR:,,{(!string.IsNullOrEmpty(_street) ? _street + " " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")},{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")},{(!string.IsNullOrEmpty(_city) ? _city : "")},{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")},{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n"; } else { - var version = outputType.ToString().Substring(5); - if (version.Length > 1) - version = version.Insert(1, "."); - else - version += ".0"; - - payload += "BEGIN:VCARD\r\n"; - payload += $"VERSION:{version}\r\n"; - - payload += $"N:{(!string.IsNullOrEmpty(lastname) ? lastname : "")};{(!string.IsNullOrEmpty(firstname) ? firstname : "")};;;\r\n"; - payload += $"FN:{(!string.IsNullOrEmpty(firstname) ? firstname + " " : "")}{(!string.IsNullOrEmpty(lastname) ? lastname : "")}\r\n"; - if (!string.IsNullOrEmpty(org)) - { - payload += $"ORG:" + org + "\r\n"; - } - if (!string.IsNullOrEmpty(orgTitle)) - { - payload += $"TITLE:" + orgTitle + "\r\n"; - } - if (!string.IsNullOrEmpty(phone)) - { - payload += $"TEL;"; - if (outputType == ContactOutputType.VCard21) - payload += $"HOME;VOICE:{phone}"; - else if (outputType == ContactOutputType.VCard3) - payload += $"TYPE=HOME,VOICE:{phone}"; - else - payload += $"TYPE=home,voice;VALUE=uri:tel:{phone}"; - payload += "\r\n"; - } - - if (!string.IsNullOrEmpty(mobilePhone)) - { - payload += $"TEL;"; - if (outputType == ContactOutputType.VCard21) - payload += $"HOME;CELL:{mobilePhone}"; - else if (outputType == ContactOutputType.VCard3) - payload += $"TYPE=HOME,CELL:{mobilePhone}"; - else - payload += $"TYPE=home,cell;VALUE=uri:tel:{mobilePhone}"; - payload += "\r\n"; - } + addressString = $"ADR:,,{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber + " " : "")}{(!string.IsNullOrEmpty(_street) ? _street : "")},{(!string.IsNullOrEmpty(_city) ? _city : "")},{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")},{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")},{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n"; + } + payload += addressString; + if (!string.IsNullOrEmpty(_website)) + payload += $"URL:{_website}\r\n"; + if (!string.IsNullOrEmpty(_nickname)) + payload += $"NICKNAME:{_nickname}\r\n"; + payload = payload.Trim(new char[] { '\r', '\n' }); + } + else + { + var version = _outputType.ToString().Substring(5); + if (version.Length > 1) + version = version.Insert(1, "."); + else + version += ".0"; - if (!string.IsNullOrEmpty(workPhone)) - { - payload += $"TEL;"; - if (outputType == ContactOutputType.VCard21) - payload += $"WORK;VOICE:{workPhone}"; - else if (outputType == ContactOutputType.VCard3) - payload += $"TYPE=WORK,VOICE:{workPhone}"; - else - payload += $"TYPE=work,voice;VALUE=uri:tel:{workPhone}"; - payload += "\r\n"; - } + payload += "BEGIN:VCARD\r\n"; + payload += $"VERSION:{version}\r\n"; + payload += $"N:{(!string.IsNullOrEmpty(_lastname) ? _lastname : "")};{(!string.IsNullOrEmpty(_firstname) ? _firstname : "")};;;\r\n"; + payload += $"FN:{(!string.IsNullOrEmpty(_firstname) ? _firstname + " " : "")}{(!string.IsNullOrEmpty(_lastname) ? _lastname : "")}\r\n"; + if (!string.IsNullOrEmpty(_org)) + { + payload += $"ORG:" + _org + "\r\n"; + } + if (!string.IsNullOrEmpty(_orgTitle)) + { + payload += $"TITLE:" + _orgTitle + "\r\n"; + } + if (!string.IsNullOrEmpty(_phone)) + { + payload += $"TEL;"; + if (_outputType == ContactOutputType.VCard21) + payload += $"HOME;VOICE:{_phone}"; + else if (_outputType == ContactOutputType.VCard3) + payload += $"TYPE=HOME,VOICE:{_phone}"; + else + payload += $"TYPE=home,voice;VALUE=uri:tel:{_phone}"; + payload += "\r\n"; + } - payload += "ADR;"; - if (outputType == ContactOutputType.VCard21) - payload += "HOME;PREF:"; - else if (outputType == ContactOutputType.VCard3) - payload += "TYPE=HOME,PREF:"; + if (!string.IsNullOrEmpty(_mobilePhone)) + { + payload += $"TEL;"; + if (_outputType == ContactOutputType.VCard21) + payload += $"HOME;CELL:{_mobilePhone}"; + else if (_outputType == ContactOutputType.VCard3) + payload += $"TYPE=HOME,CELL:{_mobilePhone}"; else - payload += "TYPE=home,pref:"; - string addressString = string.Empty; - if(addressOrder == AddressOrder.Default) - { - addressString = $";;{(!string.IsNullOrEmpty(street) ? street + " " : "")}{(!string.IsNullOrEmpty(houseNumber) ? houseNumber : "")};{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")};{(!string.IsNullOrEmpty(city) ? city : "")};{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")};{(!string.IsNullOrEmpty(country) ? country : "")}\r\n"; - } + payload += $"TYPE=home,cell;VALUE=uri:tel:{_mobilePhone}"; + payload += "\r\n"; + } + + if (!string.IsNullOrEmpty(_workPhone)) + { + payload += $"TEL;"; + if (_outputType == ContactOutputType.VCard21) + payload += $"WORK;VOICE:{_workPhone}"; + else if (_outputType == ContactOutputType.VCard3) + payload += $"TYPE=WORK,VOICE:{_workPhone}"; else - { - addressString = $";;{(!string.IsNullOrEmpty(houseNumber) ? houseNumber + " " : "")}{(!string.IsNullOrEmpty(street) ? street : "")};{(!string.IsNullOrEmpty(city) ? city : "")};{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")};{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")};{(!string.IsNullOrEmpty(country) ? country : "")}\r\n"; - } - payload += addressString; + payload += $"TYPE=work,voice;VALUE=uri:tel:{_workPhone}"; + payload += "\r\n"; + } - if (birthday != null) - payload += $"BDAY:{((DateTime)birthday).ToString("yyyyMMdd")}\r\n"; - if (!string.IsNullOrEmpty(website)) - payload += $"URL:{website}\r\n"; - if (!string.IsNullOrEmpty(email)) - payload += $"EMAIL:{email}\r\n"; - if (!string.IsNullOrEmpty(note)) - payload += $"NOTE:{note}\r\n"; - if (outputType != ContactOutputType.VCard21 && !string.IsNullOrEmpty(nickname)) - payload += $"NICKNAME:{nickname}\r\n"; - payload += "END:VCARD"; + payload += "ADR;"; + if (_outputType == ContactOutputType.VCard21) + payload += "HOME;PREF:"; + else if (_outputType == ContactOutputType.VCard3) + payload += "TYPE=HOME,PREF:"; + else + payload += "TYPE=home,pref:"; + string addressString = string.Empty; + if (_addressOrder == AddressOrder.Default) + { + addressString = $";;{(!string.IsNullOrEmpty(_street) ? _street + " " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")};{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")};{(!string.IsNullOrEmpty(_city) ? _city : "")};{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")};{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n"; + } + else + { + addressString = $";;{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber + " " : "")}{(!string.IsNullOrEmpty(_street) ? _street : "")};{(!string.IsNullOrEmpty(_city) ? _city : "")};{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")};{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")};{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n"; } + payload += addressString; + + if (_birthday != null) + payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd")}\r\n"; + if (!string.IsNullOrEmpty(_website)) + payload += $"URL:{_website}\r\n"; + if (!string.IsNullOrEmpty(_email)) + payload += $"EMAIL:{_email}\r\n"; + if (!string.IsNullOrEmpty(_note)) + payload += $"NOTE:{_note}\r\n"; + if (_outputType != ContactOutputType.VCard21 && !string.IsNullOrEmpty(_nickname)) + payload += $"NICKNAME:{_nickname}\r\n"; - return payload; + payload += "END:VCARD"; } + return payload; + } + + /// + /// Possible output types. Either vCard 2.1, vCard 3.0, vCard 4.0 or MeCard. + /// + public enum ContactOutputType + { /// - /// Possible output types. Either vCard 2.1, vCard 3.0, vCard 4.0 or MeCard. + /// MeCard output type. /// - public enum ContactOutputType - { - /// - /// MeCard output type. - /// - MeCard, + MeCard, - /// - /// vCard 2.1 output type. - /// - VCard21, + /// + /// vCard 2.1 output type. + /// + VCard21, - /// - /// vCard 3.0 output type. - /// - VCard3, + /// + /// vCard 3.0 output type. + /// + VCard3, - /// - /// vCard 4.0 output type. - /// - VCard4 - } + /// + /// vCard 4.0 output type. + /// + VCard4 + } + /// + /// Define the address format. + /// Default: European format, ([Street] [House Number] and [Postal Code] [City]) + /// Reversed: North American and others format ([House Number] [Street] and [City] [Postal Code]) + /// + public enum AddressOrder + { /// - /// Define the address format. - /// Default: European format, ([Street] [House Number] and [Postal Code] [City]) - /// Reversed: North American and others format ([House Number] [Street] and [City] [Postal Code]) + /// Default address order format (European format). /// - public enum AddressOrder - { - /// - /// Default address order format (European format). - /// - Default, + Default, - /// - /// Reversed address order format (North American and others format). - /// - Reversed - } + /// + /// Reversed address order format (North American and others format). + /// + Reversed } } } diff --git a/QRCoder/PayloadGenerator/Geolocation.cs b/QRCoder/PayloadGenerator/Geolocation.cs index 23627725..944eef1d 100644 --- a/QRCoder/PayloadGenerator/Geolocation.cs +++ b/QRCoder/PayloadGenerator/Geolocation.cs @@ -1,61 +1,54 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a geo location payload. Supports raw location (GEO encoding) or Google Maps link (GoogleMaps encoding). + /// + public class Geolocation : Payload { + private readonly string _latitude, _longitude; + private readonly GeolocationEncoding _encoding; + /// - /// Generates a geo location payload. Supports raw location (GEO encoding) or Google Maps link (GoogleMaps encoding). + /// Initializes a new instance of the class. + /// Supports raw location (GEO encoding) or Google Maps link (GoogleMaps encoding). /// - public class Geolocation : Payload + /// Latitude with . as splitter. + /// Longitude with . as splitter. + /// Encoding type - GEO or GoogleMaps. + public Geolocation(string latitude, string longitude, GeolocationEncoding encoding = GeolocationEncoding.GEO) { - private readonly string latitude, longitude; - private readonly GeolocationEncoding encoding; + _latitude = latitude.Replace(",", "."); + _longitude = longitude.Replace(",", "."); + _encoding = encoding; + } - /// - /// Initializes a new instance of the class. - /// Supports raw location (GEO encoding) or Google Maps link (GoogleMaps encoding). - /// - /// Latitude with . as splitter. - /// Longitude with . as splitter. - /// Encoding type - GEO or GoogleMaps. - public Geolocation(string latitude, string longitude, GeolocationEncoding encoding = GeolocationEncoding.GEO) - { - this.latitude = latitude.Replace(",", "."); - this.longitude = longitude.Replace(",", "."); - this.encoding = encoding; - } + /// + /// Returns a string representation of the geolocation payload. + /// + /// A string representation of the geolocation payload in the specified encoding format. + public override string ToString() => _encoding switch + { + GeolocationEncoding.GEO => $"geo:{_latitude},{_longitude}", + GeolocationEncoding.GoogleMaps => $"http://maps.google.com/maps?q={_latitude},{_longitude}", + _ => "geo:", + }; + /// + /// Defines the encoding types for geolocation payloads. + /// + public enum GeolocationEncoding + { /// - /// Returns a string representation of the geolocation payload. + /// GEO encoding type. /// - /// A string representation of the geolocation payload in the specified encoding format. - public override string ToString() - { - switch (this.encoding) - { - case GeolocationEncoding.GEO: - return $"geo:{this.latitude},{this.longitude}"; - case GeolocationEncoding.GoogleMaps: - return $"http://maps.google.com/maps?q={this.latitude},{this.longitude}"; - default: - return "geo:"; - } - } + GEO, /// - /// Defines the encoding types for geolocation payloads. + /// Google Maps encoding type. /// - public enum GeolocationEncoding - { - /// - /// GEO encoding type. - /// - GEO, - - /// - /// Google Maps encoding type. - /// - GoogleMaps - } + GoogleMaps } } } diff --git a/QRCoder/PayloadGenerator/Girocode.cs b/QRCoder/PayloadGenerator/Girocode.cs index 0cf5f54c..94c8b997 100644 --- a/QRCoder/PayloadGenerator/Girocode.cs +++ b/QRCoder/PayloadGenerator/Girocode.cs @@ -1,209 +1,208 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates the payload for a Girocode (QR-Code with credit transfer information). + /// + public class Girocode : Payload { + //Keep in mind, that the ECC level has to be set to "M" when generating a Girocode! + //Girocode specification: http://www.europeanpaymentscouncil.eu/index.cfm/knowledge-bank/epc-documents/quick-response-code-guidelines-to-enable-data-capture-for-the-initiation-of-a-sepa-credit-transfer/epc069-12-quick-response-code-guidelines-to-enable-data-capture-for-the-initiation-of-a-sepa-credit-transfer1/ + + private readonly string _br = "\n"; + private readonly string _iban, _bic, _name, _purposeOfCreditTransfer, _remittanceInformation, _messageToGirocodeUser; + private readonly decimal _amount; + private readonly GirocodeVersion _version; + private readonly GirocodeEncoding _encoding; + private readonly TypeOfRemittance _typeOfRemittance; + /// - /// Generates the payload for a Girocode (QR-Code with credit transfer information). + /// Gets the ECC level required for Girocode, which is always set to M. /// - public class Girocode : Payload + public override QRCodeGenerator.ECCLevel EccLevel => QRCodeGenerator.ECCLevel.M; + + /// + /// Initializes a new instance of the class, which contains a payload for a Girocode (QR-Code with credit transfer information). + /// + /// Account number of the Beneficiary. Only IBAN is allowed. + /// BIC of the Beneficiary Bank. + /// Name of the Beneficiary. + /// Amount of the Credit Transfer in Euro. (Amount must be more than 0.01 and less than 999999999.99) + /// Remittance Information (Purpose-/reference text). (optional) + /// Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars. + /// Purpose of the Credit Transfer (optional) + /// Beneficiary to originator information. (optional) + /// Girocode version. Either 001 or 002. Default: 001. + /// Encoding of the Girocode payload. Default: ISO-8859-1 + /// Thrown when the input values are not valid according to the Girocode specification. + public Girocode(string iban, string bic, string name, decimal amount, string remittanceInformation = "", TypeOfRemittance typeOfRemittance = TypeOfRemittance.Unstructured, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", GirocodeVersion version = GirocodeVersion.Version1, GirocodeEncoding encoding = GirocodeEncoding.ISO_8859_1) { - //Keep in mind, that the ECC level has to be set to "M" when generating a Girocode! - //Girocode specification: http://www.europeanpaymentscouncil.eu/index.cfm/knowledge-bank/epc-documents/quick-response-code-guidelines-to-enable-data-capture-for-the-initiation-of-a-sepa-credit-transfer/epc069-12-quick-response-code-guidelines-to-enable-data-capture-for-the-initiation-of-a-sepa-credit-transfer1/ + _version = version; + _encoding = encoding; + if (!IsValidIban(iban)) + throw new GirocodeException("The IBAN entered isn't valid."); + _iban = iban.Replace(" ", "").ToUpper(); + if (!IsValidBic(bic)) + throw new GirocodeException("The BIC entered isn't valid."); + _bic = bic.Replace(" ", "").ToUpper(); + if (name.Length > 70) + throw new GirocodeException("(Payee-)Name must be shorter than 71 chars."); + _name = name; + if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",", ".").Split('.')[1].TrimEnd('0').Length > 2) + throw new GirocodeException("Amount must have less than 3 digits after decimal point."); + if (amount < 0.01m || amount > 999999999.99m) + throw new GirocodeException("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); + _amount = amount; + if (purposeOfCreditTransfer.Length > 4) + throw new GirocodeException("Purpose of credit transfer can only have 4 chars at maximum."); + _purposeOfCreditTransfer = purposeOfCreditTransfer; + if (typeOfRemittance == TypeOfRemittance.Unstructured && remittanceInformation.Length > 140) + throw new GirocodeException("Unstructured reference texts have to be shorter than 141 chars."); + if (typeOfRemittance == TypeOfRemittance.Structured && remittanceInformation.Length > 35) + throw new GirocodeException("Structured reference texts have to be shorter than 36 chars."); + _typeOfRemittance = typeOfRemittance; + _remittanceInformation = remittanceInformation; + if (messageToGirocodeUser.Length > 70) + throw new GirocodeException("Message to the Girocode-User reader texts have to be shorter than 71 chars."); + _messageToGirocodeUser = messageToGirocodeUser; + } - private string br = "\n"; - private readonly string iban, bic, name, purposeOfCreditTransfer, remittanceInformation, messageToGirocodeUser; - private readonly decimal amount; - private readonly GirocodeVersion version; - private readonly GirocodeEncoding encoding; - private readonly TypeOfRemittance typeOfRemittance; + /// + /// Returns the Girocode payload as a string. + /// + /// The Girocode payload as a string. + public override string ToString() + { + var girocodePayload = "BCD" + _br; + girocodePayload += ((_version == GirocodeVersion.Version1) ? "001" : "002") + _br; + girocodePayload += (int)_encoding + 1 + _br; + girocodePayload += "SCT" + _br; + girocodePayload += _bic + _br; + girocodePayload += _name + _br; + girocodePayload += _iban + _br; + girocodePayload += $"EUR{_amount:0.00}".Replace(",", ".") + _br; + girocodePayload += _purposeOfCreditTransfer + _br; + girocodePayload += ((_typeOfRemittance == TypeOfRemittance.Structured) + ? _remittanceInformation + : string.Empty) + _br; + girocodePayload += ((_typeOfRemittance == TypeOfRemittance.Unstructured) + ? _remittanceInformation + : string.Empty) + _br; + girocodePayload += _messageToGirocodeUser; + + return ConvertStringToEncoding(girocodePayload, _encoding.ToString().Replace("_", "-")); + } + /// + /// Defines the Girocode version. + /// + public enum GirocodeVersion + { /// - /// Gets the ECC level required for Girocode, which is always set to M. + /// Girocode version 1. /// - public override QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } } + Version1, /// - /// Initializes a new instance of the class, which contains a payload for a Girocode (QR-Code with credit transfer information). + /// Girocode version 2. /// - /// Account number of the Beneficiary. Only IBAN is allowed. - /// BIC of the Beneficiary Bank. - /// Name of the Beneficiary. - /// Amount of the Credit Transfer in Euro. (Amount must be more than 0.01 and less than 999999999.99) - /// Remittance Information (Purpose-/reference text). (optional) - /// Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars. - /// Purpose of the Credit Transfer (optional) - /// Beneficiary to originator information. (optional) - /// Girocode version. Either 001 or 002. Default: 001. - /// Encoding of the Girocode payload. Default: ISO-8859-1 - /// Thrown when the input values are not valid according to the Girocode specification. - public Girocode(string iban, string bic, string name, decimal amount, string remittanceInformation = "", TypeOfRemittance typeOfRemittance = TypeOfRemittance.Unstructured, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", GirocodeVersion version = GirocodeVersion.Version1, GirocodeEncoding encoding = GirocodeEncoding.ISO_8859_1) - { - this.version = version; - this.encoding = encoding; - if (!IsValidIban(iban)) - throw new GirocodeException("The IBAN entered isn't valid."); - this.iban = iban.Replace(" ","").ToUpper(); - if (!IsValidBic(bic)) - throw new GirocodeException("The BIC entered isn't valid."); - this.bic = bic.Replace(" ", "").ToUpper(); - if (name.Length > 70) - throw new GirocodeException("(Payee-)Name must be shorter than 71 chars."); - this.name = name; - if (amount.ToString().Replace(",", ".").Contains(".") && amount.ToString().Replace(",",".").Split('.')[1].TrimEnd('0').Length > 2) - throw new GirocodeException("Amount must have less than 3 digits after decimal point."); - if (amount < 0.01m || amount > 999999999.99m) - throw new GirocodeException("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); - this.amount = amount; - if (purposeOfCreditTransfer.Length > 4) - throw new GirocodeException("Purpose of credit transfer can only have 4 chars at maximum."); - this.purposeOfCreditTransfer = purposeOfCreditTransfer; - if (typeOfRemittance == TypeOfRemittance.Unstructured && remittanceInformation.Length > 140) - throw new GirocodeException("Unstructured reference texts have to be shorter than 141 chars."); - if (typeOfRemittance == TypeOfRemittance.Structured && remittanceInformation.Length > 35) - throw new GirocodeException("Structured reference texts have to be shorter than 36 chars."); - this.typeOfRemittance = typeOfRemittance; - this.remittanceInformation = remittanceInformation; - if (messageToGirocodeUser.Length > 70) - throw new GirocodeException("Message to the Girocode-User reader texts have to be shorter than 71 chars."); - this.messageToGirocodeUser = messageToGirocodeUser; - } + Version2 + } + /// + /// Defines the type of remittance information. + /// + public enum TypeOfRemittance + { /// - /// Returns the Girocode payload as a string. + /// Structured remittance information. /// - /// The Girocode payload as a string. - public override string ToString() - { - var girocodePayload = "BCD" + br; - girocodePayload += ((version == GirocodeVersion.Version1) ? "001" : "002") + br; - girocodePayload += (int)encoding + 1 + br; - girocodePayload += "SCT" + br; - girocodePayload += bic + br; - girocodePayload += name + br; - girocodePayload += iban + br; - girocodePayload += $"EUR{amount:0.00}".Replace(",",".") + br; - girocodePayload += purposeOfCreditTransfer + br; - girocodePayload += ((typeOfRemittance == TypeOfRemittance.Structured) - ? remittanceInformation - : string.Empty) + br; - girocodePayload += ((typeOfRemittance == TypeOfRemittance.Unstructured) - ? remittanceInformation - : string.Empty) + br; - girocodePayload += messageToGirocodeUser; - - return ConvertStringToEncoding(girocodePayload, encoding.ToString().Replace("_","-")); - } + Structured, /// - /// Defines the Girocode version. + /// Unstructured remittance information. /// - public enum GirocodeVersion - { - /// - /// Girocode version 1. - /// - Version1, - - /// - /// Girocode version 2. - /// - Version2 - } + Unstructured + } + + /// + /// Defines the encoding types for Girocode payloads. + /// + public enum GirocodeEncoding + { + /// + /// UTF-8 encoding. + /// + UTF_8, + + /// + /// ISO-8859-1 encoding. + /// + ISO_8859_1, + + /// + /// ISO-8859-2 encoding. + /// + ISO_8859_2, + + /// + /// ISO-8859-4 encoding. + /// + ISO_8859_4, + + /// + /// ISO-8859-5 encoding. + /// + ISO_8859_5, + + /// + /// ISO-8859-7 encoding. + /// + ISO_8859_7, + + /// + /// ISO-8859-10 encoding. + /// + ISO_8859_10, /// - /// Defines the type of remittance information. + /// ISO-8859-15 encoding. + /// + ISO_8859_15 + } + + /// + /// Represents errors that occur during Girocode generation. + /// + public class GirocodeException : Exception + { + /// + /// Initializes a new instance of the class. /// - public enum TypeOfRemittance + public GirocodeException() { - /// - /// Structured remittance information. - /// - Structured, - - /// - /// Unstructured remittance information. - /// - Unstructured } /// - /// Defines the encoding types for Girocode payloads. + /// Initializes a new instance of the class with a specified error message. /// - public enum GirocodeEncoding + /// The message that describes the error. + public GirocodeException(string message) + : base(message) { - /// - /// UTF-8 encoding. - /// - UTF_8, - - /// - /// ISO-8859-1 encoding. - /// - ISO_8859_1, - - /// - /// ISO-8859-2 encoding. - /// - ISO_8859_2, - - /// - /// ISO-8859-4 encoding. - /// - ISO_8859_4, - - /// - /// ISO-8859-5 encoding. - /// - ISO_8859_5, - - /// - /// ISO-8859-7 encoding. - /// - ISO_8859_7, - - /// - /// ISO-8859-10 encoding. - /// - ISO_8859_10, - - /// - /// ISO-8859-15 encoding. - /// - ISO_8859_15 } /// - /// Represents errors that occur during Girocode generation. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class GirocodeException : Exception + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public GirocodeException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public GirocodeException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public GirocodeException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public GirocodeException(string message, Exception inner) - : base(message, inner) - { - } } } } diff --git a/QRCoder/PayloadGenerator/LitecoinAddress.cs b/QRCoder/PayloadGenerator/LitecoinAddress.cs index b7d0bfe5..a36615ce 100644 --- a/QRCoder/PayloadGenerator/LitecoinAddress.cs +++ b/QRCoder/PayloadGenerator/LitecoinAddress.cs @@ -1,22 +1,21 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for Litecoin payment addresses. + /// + public class LitecoinAddress : BitcoinLikeCryptoCurrencyAddress { /// - /// Generates a payload for Litecoin payment addresses. + /// Initializes a new instance of the class. + /// Generates a Litecoin payment payload. QR Codes with this payload can open a payment app. /// - public class LitecoinAddress : BitcoinLikeCryptoCurrencyAddress - { - /// - /// Initializes a new instance of the class. - /// Generates a Litecoin payment payload. QR Codes with this payload can open a payment app. - /// - /// The Litecoin address of the payment receiver. - /// The amount of Litecoin to transfer. - /// A reference label. - /// A reference text or message. - public LitecoinAddress(string address, double? amount, string? label = null, string? message = null) - : base(BitcoinLikeCryptoCurrencyType.Litecoin, address, amount, label, message) { } - } + /// The Litecoin address of the payment receiver. + /// The amount of Litecoin to transfer. + /// A reference label. + /// A reference text or message. + public LitecoinAddress(string address, double? amount, string? label = null, string? message = null) + : base(BitcoinLikeCryptoCurrencyType.Litecoin, address, amount, label, message) { } } } diff --git a/QRCoder/PayloadGenerator/MMS.cs b/QRCoder/PayloadGenerator/MMS.cs index f3b33321..f4ccdfb7 100644 --- a/QRCoder/PayloadGenerator/MMS.cs +++ b/QRCoder/PayloadGenerator/MMS.cs @@ -1,82 +1,67 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a MMS (Multimedia Messaging Service) payload for QR codes. + /// + public class MMS : Payload { + private readonly string _number, _subject; + private readonly MMSEncoding _encoding; + /// - /// Generates a MMS (Multimedia Messaging Service) payload for QR codes. + /// Creates a MMS payload without text. /// - public class MMS : Payload + /// Receiver phone number. + /// Encoding type. + public MMS(string number, MMSEncoding encoding = MMSEncoding.MMS) { - private readonly string number, subject; - private readonly MMSEncoding encoding; + _number = number; + _subject = string.Empty; + _encoding = encoding; + } - /// - /// Creates a MMS payload without text. - /// - /// Receiver phone number. - /// Encoding type. - public MMS(string number, MMSEncoding encoding = MMSEncoding.MMS) - { - this.number = number; - this.subject = string.Empty; - this.encoding = encoding; - } + /// + /// Creates a MMS payload with text (subject). + /// + /// Receiver phone number. + /// Text of the MMS. + /// Encoding type. + public MMS(string number, string subject, MMSEncoding encoding = MMSEncoding.MMS) + { + _number = number; + _subject = subject; + _encoding = encoding; + } - /// - /// Creates a MMS payload with text (subject). - /// - /// Receiver phone number. - /// Text of the MMS. - /// Encoding type. - public MMS(string number, string subject, MMSEncoding encoding = MMSEncoding.MMS) - { - this.number = number; - this.subject = subject; - this.encoding = encoding; - } + /// + /// Returns the MMS payload as a string. + /// + /// The MMS payload as a string. + public override string ToString() => _encoding switch + { + MMSEncoding.MMSTO => $"mmsto:{_number}{(string.IsNullOrEmpty(_subject) ? string.Empty : $"?subject={Uri.EscapeDataString(_subject)}")}", + MMSEncoding.MMS => $"mms:{_number}{(string.IsNullOrEmpty(_subject) ? string.Empty : $"?body={Uri.EscapeDataString(_subject)}")}", + _ => string.Empty, + }; + /// + /// Defines the encoding types for the MMS payload. + /// + public enum MMSEncoding + { /// - /// Returns the MMS payload as a string. + /// Uses the "mms:" URI scheme. /// - /// The MMS payload as a string. - public override string ToString() - { - var returnVal = string.Empty; - switch (this.encoding) - { - case MMSEncoding.MMSTO: - var queryStringMmsTo = string.Empty; - if (!string.IsNullOrEmpty(this.subject)) - queryStringMmsTo = $"?subject={Uri.EscapeDataString(this.subject)}"; - returnVal = $"mmsto:{this.number}{queryStringMmsTo}"; - break; - case MMSEncoding.MMS: - var queryStringMms = string.Empty; - if (!string.IsNullOrEmpty(this.subject)) - queryStringMms = $"?body={Uri.EscapeDataString(this.subject)}"; - returnVal = $"mms:{this.number}{queryStringMms}"; - break; - } - return returnVal; - } + MMS, /// - /// Defines the encoding types for the MMS payload. + /// Uses the "mmsto:" URI scheme. /// - public enum MMSEncoding - { - /// - /// Uses the "mms:" URI scheme. - /// - MMS, - - /// - /// Uses the "mmsto:" URI scheme. - /// - MMSTO - } + MMSTO } } } diff --git a/QRCoder/PayloadGenerator/Mail.cs b/QRCoder/PayloadGenerator/Mail.cs index fd08b91b..82bcaa92 100644 --- a/QRCoder/PayloadGenerator/Mail.cs +++ b/QRCoder/PayloadGenerator/Mail.cs @@ -1,83 +1,79 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates an email payload that can be used to create a QR code for sending an email. + /// + public class Mail : Payload { + private readonly string? _mailReceiver, _subject, _message; + private readonly MailEncoding _encoding; + + /// - /// Generates an email payload that can be used to create a QR code for sending an email. + /// Creates an email payload with subject and message/text. /// - public class Mail : Payload + /// Receiver's email address. + /// Subject line of the email. + /// Message content of the email. + /// Payload encoding type. Choose dependent on your QR Code scanner app. + public Mail(string? mailReceiver = null, string? subject = null, string? message = null, MailEncoding encoding = MailEncoding.MAILTO) { - private readonly string? mailReceiver, subject, message; - private readonly MailEncoding encoding; - + _mailReceiver = mailReceiver; + _subject = subject; + _message = message; + _encoding = encoding; + } - /// - /// Creates an email payload with subject and message/text. - /// - /// Receiver's email address. - /// Subject line of the email. - /// Message content of the email. - /// Payload encoding type. Choose dependent on your QR Code scanner app. - public Mail(string? mailReceiver = null, string? subject = null, string? message = null, MailEncoding encoding = MailEncoding.MAILTO) + /// + /// Returns the email payload as a string. + /// + /// The email payload as a string. + public override string ToString() + { + switch (_encoding) { - this.mailReceiver = mailReceiver; - this.subject = subject; - this.message = message; - this.encoding = encoding; + case MailEncoding.MAILTO: + var parts = new List(); + if (!string.IsNullOrEmpty(_subject)) + parts.Add("subject=" + Uri.EscapeDataString(_subject)); + if (!string.IsNullOrEmpty(_message)) + parts.Add("body=" + Uri.EscapeDataString(_message)); + var queryString = parts.Any() ? $"?{string.Join("&", parts.ToArray())}" : ""; + return $"mailto:{_mailReceiver}{queryString}"; + case MailEncoding.MATMSG: + return $"MATMSG:TO:{_mailReceiver};SUB:{EscapeInput(_subject ?? "")};BODY:{EscapeInput(_message ?? "")};;"; + case MailEncoding.SMTP: + return $"SMTP:{_mailReceiver}:{EscapeInput(_subject ?? "", true)}:{EscapeInput(_message ?? "", true)}"; + default: + return string.Empty; } + } + /// + /// Defines the encoding types for the email payload. + /// + public enum MailEncoding + { /// - /// Returns the email payload as a string. + /// Uses the "mailto:" URI scheme. /// - /// The email payload as a string. - public override string ToString() - { - var returnVal = string.Empty; - switch (this.encoding) - { - case MailEncoding.MAILTO: - var parts = new List(); - if (!string.IsNullOrEmpty(this.subject)) - parts.Add("subject=" + Uri.EscapeDataString(this.subject)); - if (!string.IsNullOrEmpty(this.message)) - parts.Add("body=" + Uri.EscapeDataString(this.message)); - var queryString = parts.Any() ? $"?{string.Join("&", parts.ToArray())}" : ""; - returnVal = $"mailto:{this.mailReceiver}{queryString}"; - break; - case MailEncoding.MATMSG: - returnVal = $"MATMSG:TO:{this.mailReceiver};SUB:{EscapeInput(this.subject ?? "")};BODY:{EscapeInput(this.message ?? "")};;"; - break; - case MailEncoding.SMTP: - returnVal = $"SMTP:{this.mailReceiver}:{EscapeInput(this.subject ?? "", true)}:{EscapeInput(this.message ?? "", true)}"; - break; - } - return returnVal; - } + MAILTO, /// - /// Defines the encoding types for the email payload. + /// Uses the "MATMSG:" format. /// - public enum MailEncoding - { - /// - /// Uses the "mailto:" URI scheme. - /// - MAILTO, - - /// - /// Uses the "MATMSG:" format. - /// - MATMSG, + MATMSG, - /// - /// Uses the "SMTP:" format. - /// - SMTP - } + /// + /// Uses the "SMTP:" format. + /// + SMTP } } } diff --git a/QRCoder/PayloadGenerator/MoneroTransaction.cs b/QRCoder/PayloadGenerator/MoneroTransaction.cs index e40b84d8..8befa4f9 100644 --- a/QRCoder/PayloadGenerator/MoneroTransaction.cs +++ b/QRCoder/PayloadGenerator/MoneroTransaction.cs @@ -1,85 +1,84 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a Monero transaction payload for QR codes. + /// + public class MoneroTransaction : Payload { + private readonly string _address; + private readonly string? _txPaymentId, _recipientName, _txDescription; + private readonly float? _txAmount; + + /// + /// Creates a Monero transaction payload. + /// + /// Receiver's Monero address. + /// Amount to transfer. + /// Payment ID. + /// Recipient's name. + /// Reference text / payment description. + /// Thrown when the address is null or empty, or when the txAmount is less than or equal to 0. + public MoneroTransaction(string address, float? txAmount = null, string? txPaymentId = null, string? recipientName = null, string? txDescription = null) + { + if (string.IsNullOrEmpty(address)) + throw new MoneroTransactionException("The address is mandatory and has to be set."); + _address = address; + if (txAmount != null && txAmount <= 0) + throw new MoneroTransactionException("Value of 'txAmount' must be greater than 0."); + _txAmount = txAmount; + _txPaymentId = txPaymentId; + _recipientName = recipientName; + _txDescription = txDescription; + } + /// - /// Generates a Monero transaction payload for QR codes. + /// Returns the Monero transaction payload as a URI string. /// - public class MoneroTransaction : Payload + /// The Monero transaction payload as a URI string. + public override string ToString() { - private readonly string address; - private readonly string? txPaymentId, recipientName, txDescription; - private readonly float? txAmount; + var moneroUri = $"monero://{_address}{(!string.IsNullOrEmpty(_txPaymentId) || !string.IsNullOrEmpty(_recipientName) || !string.IsNullOrEmpty(_txDescription) || _txAmount != null ? "?" : string.Empty)}"; + moneroUri += (!string.IsNullOrEmpty(_txPaymentId) ? $"tx_payment_id={Uri.EscapeDataString(_txPaymentId)}&" : string.Empty); + moneroUri += (!string.IsNullOrEmpty(_recipientName) ? $"recipient_name={Uri.EscapeDataString(_recipientName)}&" : string.Empty); + moneroUri += (_txAmount != null ? $"tx_amount={_txAmount.ToString()!.Replace(",", ".")}&" : string.Empty); + moneroUri += (!string.IsNullOrEmpty(_txDescription) ? $"tx_description={Uri.EscapeDataString(_txDescription)}" : string.Empty); + return moneroUri.TrimEnd('&'); + } + + /// + /// Exception class for Monero transaction errors. + /// + public class MoneroTransactionException : Exception + { /// - /// Creates a Monero transaction payload. + /// Initializes a new instance of the class. /// - /// Receiver's Monero address. - /// Amount to transfer. - /// Payment ID. - /// Recipient's name. - /// Reference text / payment description. - /// Thrown when the address is null or empty, or when the txAmount is less than or equal to 0. - public MoneroTransaction(string address, float? txAmount = null, string? txPaymentId = null, string? recipientName = null, string? txDescription = null) + public MoneroTransactionException() { - if (string.IsNullOrEmpty(address)) - throw new MoneroTransactionException("The address is mandatory and has to be set."); - this.address = address; - if (txAmount != null && txAmount <= 0) - throw new MoneroTransactionException("Value of 'txAmount' must be greater than 0."); - this.txAmount = txAmount; - this.txPaymentId = txPaymentId; - this.recipientName = recipientName; - this.txDescription = txDescription; } /// - /// Returns the Monero transaction payload as a URI string. + /// Initializes a new instance of the class with a specified error message. /// - /// The Monero transaction payload as a URI string. - public override string ToString() + /// The message that describes the error. + public MoneroTransactionException(string message) + : base(message) { - var moneroUri = $"monero://{address}{(!string.IsNullOrEmpty(txPaymentId) || !string.IsNullOrEmpty(recipientName) || !string.IsNullOrEmpty(txDescription) || txAmount != null ? "?" : string.Empty)}"; - moneroUri += (!string.IsNullOrEmpty(txPaymentId) ? $"tx_payment_id={Uri.EscapeDataString(txPaymentId)}&" : string.Empty); - moneroUri += (!string.IsNullOrEmpty(recipientName) ? $"recipient_name={Uri.EscapeDataString(recipientName)}&" : string.Empty); - moneroUri += (txAmount != null ? $"tx_amount={txAmount.ToString()!.Replace(",",".")}&" : string.Empty); - moneroUri += (!string.IsNullOrEmpty(txDescription) ? $"tx_description={Uri.EscapeDataString(txDescription)}" : string.Empty); - return moneroUri.TrimEnd('&'); } - /// - /// Exception class for Monero transaction errors. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class MoneroTransactionException : Exception + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public MoneroTransactionException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public MoneroTransactionException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public MoneroTransactionException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The message that describes the error. - /// The exception that is the cause of the current exception. - public MoneroTransactionException(string message, Exception inner) - : base(message, inner) - { - } } } } diff --git a/QRCoder/PayloadGenerator/OneTimePassword.cs b/QRCoder/PayloadGenerator/OneTimePassword.cs index 3d55dca6..5cda93db 100644 --- a/QRCoder/PayloadGenerator/OneTimePassword.cs +++ b/QRCoder/PayloadGenerator/OneTimePassword.cs @@ -1,211 +1,204 @@ -using System; +using System; using System.Text; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for One Time Password (OTP) used in applications like Google Authenticator. + /// + public class OneTimePassword : Payload { + //https://github.com/google/google-authenticator/wiki/Key-Uri-Format + /// - /// Generates a payload for One Time Password (OTP) used in applications like Google Authenticator. + /// The type of OTP (TOTP or HOTP). /// - public class OneTimePassword : Payload + public OneTimePasswordAuthType Type { get; set; } = OneTimePasswordAuthType.TOTP; + + /// + /// The secret key used for OTP generation. + /// + public string Secret { get; set; } = null!; + + /// + /// The hashing algorithm used for OTP (SHA1, SHA256, SHA512). + /// + public OneTimePasswordAuthAlgorithm AuthAlgorithm { get; set; } = OneTimePasswordAuthAlgorithm.SHA1; + + /// + /// Obsolete. Use instead. + /// + [Obsolete("This property is obsolete, use " + nameof(AuthAlgorithm) + " instead", false)] + public OoneTimePasswordAuthAlgorithm Algorithm { - //https://github.com/google/google-authenticator/wiki/Key-Uri-Format - - /// - /// The type of OTP (TOTP or HOTP). - /// - public OneTimePasswordAuthType Type { get; set; } = OneTimePasswordAuthType.TOTP; - - /// - /// The secret key used for OTP generation. - /// - public string Secret { get; set; } = null!; - - /// - /// The hashing algorithm used for OTP (SHA1, SHA256, SHA512). - /// - public OneTimePasswordAuthAlgorithm AuthAlgorithm { get; set; } = OneTimePasswordAuthAlgorithm.SHA1; - - /// - /// Obsolete. Use instead. - /// - [Obsolete("This property is obsolete, use " + nameof(AuthAlgorithm) + " instead", false)] - public OoneTimePasswordAuthAlgorithm Algorithm - { - get { return (OoneTimePasswordAuthAlgorithm)Enum.Parse(typeof(OoneTimePasswordAuthAlgorithm), AuthAlgorithm.ToString()); } - set { AuthAlgorithm = (OneTimePasswordAuthAlgorithm)Enum.Parse(typeof(OneTimePasswordAuthAlgorithm), value.ToString()); } - } + get => (OoneTimePasswordAuthAlgorithm)Enum.Parse(typeof(OoneTimePasswordAuthAlgorithm), AuthAlgorithm.ToString()); + set => AuthAlgorithm = (OneTimePasswordAuthAlgorithm)Enum.Parse(typeof(OneTimePasswordAuthAlgorithm), value.ToString()); + } - /// - /// The issuer of the OTP (usually the service or company name). - /// - public string? Issuer { get; set; } - - /// - /// The label for the OTP (usually the user's email or username). - /// - public string? Label { get; set; } - - /// - /// The number of digits in the OTP (default is 6). - /// - public int Digits { get; set; } = 6; - - /// - /// The counter value for HOTP (only used if Type is HOTP). - /// - public int? Counter { get; set; } = null; - - /// - /// The period in seconds for TOTP (default is 30). - /// - public int? Period { get; set; } = 30; - - /// - /// Enum for OTP types. - /// - public enum OneTimePasswordAuthType - { - TOTP, - HOTP, - } + /// + /// The issuer of the OTP (usually the service or company name). + /// + public string? Issuer { get; set; } - /// - /// Enum for hashing algorithms used in OTP. - /// - public enum OneTimePasswordAuthAlgorithm - { - SHA1, - SHA256, - SHA512, - } + /// + /// The label for the OTP (usually the user's email or username). + /// + public string? Label { get; set; } - /// - /// Obsolete. Use instead. - /// - [Obsolete("This enum is obsolete, use " + nameof(OneTimePasswordAuthAlgorithm) + " instead", false)] - public enum OoneTimePasswordAuthAlgorithm - { - SHA1, - SHA256, - SHA512, - } + /// + /// The number of digits in the OTP (default is 6). + /// + public int Digits { get; set; } = 6; - /// - /// Returns the OTP payload as a string. - /// - /// The OTP payload as a string. - public override string ToString() - { - switch (Type) - { - case OneTimePasswordAuthType.TOTP: - return TimeToString(); - case OneTimePasswordAuthType.HOTP: - return HMACToString(); - default: - throw new ArgumentOutOfRangeException(); - } - } + /// + /// The counter value for HOTP (only used if Type is HOTP). + /// + public int? Counter { get; set; } = null; - // Note: Issuer:Label must only contain 1 : if either of the Issuer or the Label has a : then it is invalid. - // Defaults are 6 digits and 30 for Period + /// + /// The period in seconds for TOTP (default is 30). + /// + public int? Period { get; set; } = 30; - /// - /// Returns the HOTP payload as a string. - /// - /// The HOTP payload as a string. - private string HMACToString() + /// + /// Enum for OTP types. + /// + public enum OneTimePasswordAuthType + { + TOTP, + HOTP, + } + + /// + /// Enum for hashing algorithms used in OTP. + /// + public enum OneTimePasswordAuthAlgorithm + { + SHA1, + SHA256, + SHA512, + } + + /// + /// Obsolete. Use instead. + /// + [Obsolete("This enum is obsolete, use " + nameof(OneTimePasswordAuthAlgorithm) + " instead", false)] + public enum OoneTimePasswordAuthAlgorithm + { + SHA1, + SHA256, + SHA512, + } + + /// + /// Returns the OTP payload as a string. + /// + /// The OTP payload as a string. + public override string ToString() => Type switch + { + OneTimePasswordAuthType.TOTP => TimeToString(), + OneTimePasswordAuthType.HOTP => HMACToString(), + _ => throw new ArgumentOutOfRangeException(), + }; + + // Note: Issuer:Label must only contain 1 : if either of the Issuer or the Label has a : then it is invalid. + // Defaults are 6 digits and 30 for Period + + /// + /// Returns the HOTP payload as a string. + /// + /// The HOTP payload as a string. + private string HMACToString() + { + var sb = new StringBuilder("otpauth://hotp/"); + ProcessCommonFields(sb); + var actualCounter = Counter ?? 1; + sb.Append("&counter=" + actualCounter); + return sb.ToString(); + } + + /// + /// Returns the TOTP payload as a string. + /// + /// The TOTP payload as a string. + private string TimeToString() + { + if (Period == null) { - var sb = new StringBuilder("otpauth://hotp/"); - ProcessCommonFields(sb); - var actualCounter = Counter ?? 1; - sb.Append("&counter=" + actualCounter); - return sb.ToString(); + throw new Exception("Period must be set when using OneTimePasswordAuthType.TOTP"); } - /// - /// Returns the TOTP payload as a string. - /// - /// The TOTP payload as a string. - private string TimeToString() - { - if (Period == null) - { - throw new Exception("Period must be set when using OneTimePasswordAuthType.TOTP"); - } + var sb = new StringBuilder("otpauth://totp/"); - var sb = new StringBuilder("otpauth://totp/"); + ProcessCommonFields(sb); - ProcessCommonFields(sb); + if (Period != 30) + { + sb.Append("&period=" + Period); + } - if (Period != 30) - { - sb.Append("&period=" + Period); - } + return sb.ToString(); + } - return sb.ToString(); + /// + /// Processes the common fields for both TOTP and HOTP. + /// + /// StringBuilder to append the common fields. + private void ProcessCommonFields(StringBuilder sb) + { + if (Secret.IsNullOrWhiteSpace()) + { + throw new Exception("Secret must be a filled out base32 encoded string"); } + string strippedSecret = Secret.Replace(" ", ""); + string? escapedIssuer = null; + string? escapedLabel = null; + string? label = null; - /// - /// Processes the common fields for both TOTP and HOTP. - /// - /// StringBuilder to append the common fields. - private void ProcessCommonFields(StringBuilder sb) + if (!Issuer.IsNullOrWhiteSpace()) { - if (Secret.IsNullOrWhiteSpace()) + if (Issuer.Contains(":")) { - throw new Exception("Secret must be a filled out base32 encoded string"); - } - string strippedSecret = Secret.Replace(" ", ""); - string? escapedIssuer = null; - string? escapedLabel = null; - string? label = null; - - if (!Issuer.IsNullOrWhiteSpace()) - { - if (Issuer.Contains(":")) - { - throw new Exception("Issuer must not have a ':'"); - } - escapedIssuer = Uri.EscapeDataString(Issuer); + throw new Exception("Issuer must not have a ':'"); } + escapedIssuer = Uri.EscapeDataString(Issuer); + } - if (!Label.IsNullOrWhiteSpace()) + if (!Label.IsNullOrWhiteSpace()) + { + if (Label.Contains(":")) { - if (Label.Contains(":")) - { - throw new Exception("Label must not have a ':'"); - } - escapedLabel = Uri.EscapeDataString(Label); + throw new Exception("Label must not have a ':'"); } + escapedLabel = Uri.EscapeDataString(Label); + } - if (escapedLabel != null && escapedIssuer != null) - { - label = escapedIssuer + ":" + escapedLabel; - } - else if (escapedIssuer != null) - { - label = escapedIssuer; - } + if (escapedLabel != null && escapedIssuer != null) + { + label = escapedIssuer + ":" + escapedLabel; + } + else if (escapedIssuer != null) + { + label = escapedIssuer; + } - if (label != null) - { - sb.Append(label); - } + if (label != null) + { + sb.Append(label); + } - sb.Append("?secret=" + strippedSecret); + sb.Append("?secret=" + strippedSecret); - if (escapedIssuer != null) - { - sb.Append("&issuer=" + escapedIssuer); - } + if (escapedIssuer != null) + { + sb.Append("&issuer=" + escapedIssuer); + } - if (Digits != 6) - { - sb.Append("&digits=" + Digits); - } + if (Digits != 6) + { + sb.Append("&digits=" + Digits); } } } diff --git a/QRCoder/PayloadGenerator/Payload.cs b/QRCoder/PayloadGenerator/Payload.cs index 180df7ad..0b6fa22b 100644 --- a/QRCoder/PayloadGenerator/Payload.cs +++ b/QRCoder/PayloadGenerator/Payload.cs @@ -1,35 +1,34 @@ -namespace QRCoder +namespace QRCoder; + +/// +/// Contains classes and methods for generating payloads for QR codes. +/// +public static partial class PayloadGenerator { /// - /// Contains classes and methods for generating payloads for QR codes. + /// Represents the base class for all QR code payloads. /// - public static partial class PayloadGenerator + public abstract class Payload { /// - /// Represents the base class for all QR code payloads. + /// Gets the version of the QR code payload. /// - public abstract class Payload - { - /// - /// Gets the version of the QR code payload. - /// - public virtual int Version { get { return -1; } } + public virtual int Version => -1; - /// - /// Gets the error correction level of the QR code payload. - /// - public virtual QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.Default; } } + /// + /// Gets the error correction level of the QR code payload. + /// + public virtual QRCodeGenerator.ECCLevel EccLevel => QRCodeGenerator.ECCLevel.Default; - /// - /// Gets the ECI mode of the QR code payload. - /// - public virtual QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Default; } } + /// + /// Gets the ECI mode of the QR code payload. + /// + public virtual QRCodeGenerator.EciMode EciMode => QRCodeGenerator.EciMode.Default; - /// - /// Returns a string representation of the QR code payload. - /// - /// A string representation of the QR code payload. - public abstract override string ToString(); - } + /// + /// Returns a string representation of the QR code payload. + /// + /// A string representation of the QR code payload. + public abstract override string ToString(); } } diff --git a/QRCoder/PayloadGenerator/PhoneNumber.cs b/QRCoder/PayloadGenerator/PhoneNumber.cs index 2092fc07..5627a979 100644 --- a/QRCoder/PayloadGenerator/PhoneNumber.cs +++ b/QRCoder/PayloadGenerator/PhoneNumber.cs @@ -1,31 +1,28 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a phone call payload. + /// + public class PhoneNumber : Payload { + private readonly string _number; + /// - /// Generates a phone call payload. + /// Initializes a new instance of the class. /// - public class PhoneNumber : Payload + /// Phone number of the receiver. + public PhoneNumber(string number) { - private readonly string number; - - /// - /// Initializes a new instance of the class. - /// - /// Phone number of the receiver. - public PhoneNumber(string number) - { - this.number = number; - } - - /// - /// Returns the phone call payload as a string. - /// - /// The phone call payload as a string. - public override string ToString() - { - return $"tel:{this.number}"; - } + _number = number; } + + /// + /// Returns the phone call payload as a string. + /// + /// The phone call payload as a string. + public override string ToString() + => $"tel:{_number}"; } } diff --git a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs index 3121c671..a6b1e1b8 100644 --- a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs +++ b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -7,636 +7,637 @@ using System.Reflection; #endif -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a payload for a Russian payment order. + /// + public class RussiaPaymentOrder : Payload { + // Specification of RussianPaymentOrder + //https://docs.cntd.ru/document/1200110981 + //https://roskazna.gov.ru/upload/iblock/5fa/gost_r_56042_2014.pdf + //https://sbqr.ru/standard/files/standart.pdf + + // Specification of data types described in the above standard + // https://gitea.sergeybochkov.com/bochkov/emuik/src/commit/d18f3b550f6415ea4a4a5e6097eaab4661355c72/template/ed + + // Tool for QR validation + // https://www.sbqr.ru/validator/index.html + + //base + private readonly CharacterSets _characterSet; + private readonly MandatoryFields _mFields = new MandatoryFields(); + private readonly OptionalFields _oFields = new OptionalFields(); + private string _separator = "|"; + + private RussiaPaymentOrder() + { + } + /// - /// Generates a payload for a Russian payment order. + /// Initializes a new instance of the class. /// - public class RussiaPaymentOrder : Payload + /// Name of the payee (Наименование получателя платежа) + /// Beneficiary account number (Номер счета получателя платежа) + /// Name of the beneficiary's bank (Наименование банка получателя платежа) + /// BIC (БИК) + /// Box number / account payee's bank (Номер кор./сч. банка получателя платежа) + /// An (optional) object of additional fields + /// Type of encoding (default UTF-8) + public RussiaPaymentOrder(string name, string personalAcc, string bankName, string BIC, string correspAcc, OptionalFields? optionalFields = null, CharacterSets characterSet = CharacterSets.utf_8) : this() { - // Specification of RussianPaymentOrder - //https://docs.cntd.ru/document/1200110981 - //https://roskazna.gov.ru/upload/iblock/5fa/gost_r_56042_2014.pdf - //https://sbqr.ru/standard/files/standart.pdf + _characterSet = characterSet; + _mFields.Name = ValidateInput(name, "Name", @"^.{1,160}$"); + _mFields.PersonalAcc = ValidateInput(personalAcc, "PersonalAcc", @"^[1-9]\d{4}[0-9ABCEHKMPTX]\d{14}$"); + _mFields.BankName = ValidateInput(bankName, "BankName", @"^.{1,45}$"); + _mFields.BIC = ValidateInput(BIC, "BIC", @"^\d{9}$"); + _mFields.CorrespAcc = ValidateInput(correspAcc, "CorrespAcc", @"^[1-9]\d{4}[0-9ABCEHKMPTX]\d{14}$"); + + if (optionalFields != null) + _oFields = optionalFields; + } + + /// + /// Returns the payload as a string. + /// + /// ⚠ Attention: If CharacterSets was set to windows-1251 or koi8-r you should use ToBytes() instead of ToString() and pass the bytes to CreateQrCode()! + /// The payload as a string. + public override string ToString() + { + var cp = _characterSet.ToString().Replace("_", "-"); + var bytes = ToBytes(); + +#if !NETFRAMEWORK + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif + return Encoding.GetEncoding(cp).GetString(bytes, 0, bytes.Length); + } + + /// + /// Returns the payload as a byte array. + /// + /// Should be used if CharacterSets equals windows-1251 or koi8-r. + /// The payload as a byte array. + + public byte[] ToBytes() + { + //Setup byte encoder + //Encode return string as byte[] with correct CharacterSet +#if !NETFRAMEWORK + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +#endif + var cp = _characterSet.ToString().Replace("_", "-"); + + //Calculate the seperator + _separator = DetermineSeparator(); + + //Create the payload string + string ret = $"ST0001" + ((int)_characterSet).ToString() + //(separator != "|" ? separator : "") + + $"{_separator}Name={_mFields.Name}" + + $"{_separator}PersonalAcc={_mFields.PersonalAcc}" + + $"{_separator}BankName={_mFields.BankName}" + + $"{_separator}BIC={_mFields.BIC}" + + $"{_separator}CorrespAcc={_mFields.CorrespAcc}"; + + //Check length of mandatory field block (-8 => Removing service data block bytes from ret length) + int bytesMandatoryLen = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret)).Length - 8; + if (bytesMandatoryLen > 300) + throw new RussiaPaymentOrderException($"Data too long. Mandatory data must not exceed 300 bytes, but actually is {bytesMandatoryLen} bytes long. Remove additional data fields or shorten strings/values."); - // Specification of data types described in the above standard - // https://gitea.sergeybochkov.com/bochkov/emuik/src/commit/d18f3b550f6415ea4a4a5e6097eaab4661355c72/template/ed - // Tool for QR validation - // https://www.sbqr.ru/validator/index.html + //Add optional fields, if filled + var optionalFieldsList = GetOptionalFieldsAsList(); + if (optionalFieldsList.Count > 0) + ret += $"|{string.Join("|", optionalFieldsList.ToArray())}"; + ret += _separator; - //base - private CharacterSets characterSet; - private readonly MandatoryFields mFields = new MandatoryFields(); - private readonly OptionalFields oFields = new OptionalFields(); - private string separator = "|"; + return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret)); + } + + + /// + /// Determines a valid separator + /// + /// + private string DetermineSeparator() + { + // See chapter 5.2.1 of Standard (https://sbqr.ru/standard/files/standart.pdf) + + var mandatoryValues = GetMandatoryFieldsAsList(); + var optionalValues = GetOptionalFieldsAsList(); - private RussiaPaymentOrder() + // Possible candidates for field separation + var separatorCandidates = new string[] { "|", "#", ";", ":", "^", "_", "~", "{", "}", "!", "#", "$", "%", "&", "(", ")", "*", "+", ",", "/", "@" }; + foreach (var sepCandidate in separatorCandidates) { + if (!mandatoryValues.Any(x => x.Contains(sepCandidate)) && !optionalValues.Any(x => x.Contains(sepCandidate))) + return sepCandidate; } + throw new RussiaPaymentOrderException("No valid separator found."); + } + /// + /// Takes all optional fields that are not null and returns their string represantion + /// + /// A List of strings + private List GetOptionalFieldsAsList() + { +#if NETSTANDARD1_3 + return typeof(OptionalFields).GetRuntimeProperties() + .Where(field => field.GetValue(_oFields) != null) + .Select(field => + { + var objValue = field.GetValue(_oFields, null); + var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue).ToString("dd.MM.yyyy") : objValue.ToString(); + return $"{field.Name}={value}"; + }) + .ToList(); +#else + return typeof(OptionalFields).GetProperties() + .Where(field => field.GetValue(_oFields, null) != null) + .Select(field => + { + var objValue = field.GetValue(_oFields, null); + var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); + return $"{field.Name}={value}"; + }) + .ToList(); +#endif + } + + + /// + /// Takes all mandatory fields that are not null and returns their string represantion + /// + /// A List of strings + private List GetMandatoryFieldsAsList() + { +#if NETSTANDARD1_3 + return typeof(MandatoryFields).GetRuntimeFields() + .Where(field => field.GetValue(_mFields) != null) + .Select(field => + { + var objValue = field.GetValue(_mFields); + var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue).ToString("dd.MM.yyyy") : objValue.ToString(); + return $"{field.Name}={value}"; + }) + .ToList(); +#else + return typeof(MandatoryFields).GetFields() + .Where(field => field.GetValue(_mFields) != null) + .Select(field => + { + var objValue = field.GetValue(_mFields); + var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); + return $"{field.Name}={value}"; + }) + .ToList(); +#endif + } + + /// + /// Validates a string against a given Regex pattern. Returns input if it matches the Regex expression (=valid) or throws Exception in case there's a mismatch + /// + /// String to be validated + /// Name/descriptor of the string to be validated + /// A regex pattern to be used for validation + /// An optional error text. If null, a standard error text is generated + /// Input value (in case it is valid) + private static string ValidateInput(string input, string fieldname, string pattern, string? errorText = null) + => ValidateInput(input, fieldname, new string[] { pattern }, errorText); + + /// + /// Validates a string against one or more given Regex patterns. Returns input if it matches all regex expressions (=valid) or throws Exception in case there's a mismatch + /// + /// String to be validated + /// Name/descriptor of the string to be validated + /// An array of regex patterns to be used for validation + /// An optional error text. If null, a standard error text is generated + /// Input value (in case it is valid) + private static string ValidateInput(string input, string fieldname, string[] patterns, string? errorText = null) + { + if (input == null) + throw new RussiaPaymentOrderException($"The input for '{fieldname}' must not be null."); + foreach (var pattern in patterns) + { + if (!Regex.IsMatch(input, pattern)) + throw new RussiaPaymentOrderException(errorText ?? $"The input for '{fieldname}' ({input}) doesn't match the pattern {pattern}"); + } + return input; + } + + private class MandatoryFields + { + public string Name = null!; + public string PersonalAcc = null!; + public string BankName = null!; + public string BIC = null!; + public string CorrespAcc = null!; + } + + /// + /// Represents optional fields for the RussiaPaymentOrder. + /// + public class OptionalFields + { + private string? _sum; /// - /// Initializes a new instance of the class. + /// Payment amount, in kopecks (FTI’s Amount.) + /// Сумма платежа, в копейках /// - /// Name of the payee (Наименование получателя платежа) - /// Beneficiary account number (Номер счета получателя платежа) - /// Name of the beneficiary's bank (Наименование банка получателя платежа) - /// BIC (БИК) - /// Box number / account payee's bank (Номер кор./сч. банка получателя платежа) - /// An (optional) object of additional fields - /// Type of encoding (default UTF-8) - public RussiaPaymentOrder(string name, string personalAcc, string bankName, string BIC, string correspAcc, OptionalFields? optionalFields = null, CharacterSets characterSet = CharacterSets.utf_8) : this() + public string? Sum { - this.characterSet = characterSet; - mFields.Name = ValidateInput(name, "Name", @"^.{1,160}$"); - mFields.PersonalAcc = ValidateInput(personalAcc, "PersonalAcc", @"^[1-9]\d{4}[0-9ABCEHKMPTX]\d{14}$"); - mFields.BankName = ValidateInput(bankName, "BankName", @"^.{1,45}$"); - mFields.BIC = ValidateInput(BIC, "BIC", @"^\d{9}$"); - mFields.CorrespAcc = ValidateInput(correspAcc, "CorrespAcc", @"^[1-9]\d{4}[0-9ABCEHKMPTX]\d{14}$"); - - if (optionalFields != null) - oFields = optionalFields; + get => _sum; + set => _sum = value == null ? null : ValidateInput(value, "Sum", @"^\d{1,18}$"); } + private string? _purpose; /// - /// Returns the payload as a string. + /// Payment name (purpose) + /// Наименование платежа (назначение) /// - /// ⚠ Attention: If CharacterSets was set to windows-1251 or koi8-r you should use ToBytes() instead of ToString() and pass the bytes to CreateQrCode()! - /// The payload as a string. - public override string ToString() + public string? Purpose { - var cp = characterSet.ToString().Replace("_", "-"); - var bytes = ToBytes(); - -#if !NETFRAMEWORK - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); -#endif - return Encoding.GetEncoding(cp).GetString(bytes, 0, bytes.Length); + get => _purpose; + set => _purpose = value == null ? null : ValidateInput(value, "Purpose", @"^.{1,160}$"); } + private string? _payeeInn; /// - /// Returns the payload as a byte array. + /// Payee's INN (Resident Tax Identification Number; Text, up to 12 characters.) + /// ИНН получателя платежа /// - /// Should be used if CharacterSets equals windows-1251 or koi8-r. - /// The payload as a byte array. - - public byte[] ToBytes() + public string? PayeeINN { - //Setup byte encoder - //Encode return string as byte[] with correct CharacterSet -#if !NETFRAMEWORK - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); -#endif - var cp = this.characterSet.ToString().Replace("_", "-"); - - //Calculate the seperator - separator = DetermineSeparator(); - - //Create the payload string - string ret = $"ST0001" + ((int)characterSet).ToString() + //(separator != "|" ? separator : "") + - $"{separator}Name={mFields.Name}" + - $"{separator}PersonalAcc={mFields.PersonalAcc}" + - $"{separator}BankName={mFields.BankName}" + - $"{separator}BIC={mFields.BIC}" + - $"{separator}CorrespAcc={mFields.CorrespAcc}"; - - //Check length of mandatory field block (-8 => Removing service data block bytes from ret length) - int bytesMandatoryLen = Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret)).Length - 8; - if (bytesMandatoryLen > 300) - throw new RussiaPaymentOrderException($"Data too long. Mandatory data must not exceed 300 bytes, but actually is {bytesMandatoryLen} bytes long. Remove additional data fields or shorten strings/values."); - - - //Add optional fields, if filled - var optionalFieldsList = GetOptionalFieldsAsList(); - if (optionalFieldsList.Count > 0) - ret += $"|{string.Join("|", optionalFieldsList.ToArray())}"; - ret += separator; - - return Encoding.Convert(Encoding.UTF8, Encoding.GetEncoding(cp), Encoding.UTF8.GetBytes(ret)); + get => _payeeInn; + set => _payeeInn = value == null ? null : ValidateInput(value, "PayeeINN", @"^.{1,12}$"); } - + private string? _payerInn; /// - /// Determines a valid separator + /// Payer's INN (Resident Tax Identification Number; Text, up to 12 characters.) + /// ИНН плательщика /// - /// - private string DetermineSeparator() + public string? PayerINN { - // See chapter 5.2.1 of Standard (https://sbqr.ru/standard/files/standart.pdf) - - var mandatoryValues = GetMandatoryFieldsAsList(); - var optionalValues = GetOptionalFieldsAsList(); - - // Possible candidates for field separation - var separatorCandidates = new string[]{ "|", "#", ";", ":", "^", "_", "~", "{", "}", "!", "#", "$", "%", "&", "(", ")", "*", "+", ",", "/", "@" }; - foreach (var sepCandidate in separatorCandidates) - { - if (!mandatoryValues.Any(x => x.Contains(sepCandidate)) && !optionalValues.Any(x => x.Contains(sepCandidate))) - return sepCandidate; - } - throw new RussiaPaymentOrderException("No valid separator found."); + get => _payerInn; + set => _payerInn = value == null ? null : ValidateInput(value, "PayerINN", @"^.{1,12}$"); } + private string? _drawerStatus; /// - /// Takes all optional fields that are not null and returns their string represantion + /// Status compiler payment document + /// Статус составителя платежного документа /// - /// A List of strings - private List GetOptionalFieldsAsList() + public string? DrawerStatus { -#if NETSTANDARD1_3 - return typeof(OptionalFields).GetRuntimeProperties() - .Where(field => field.GetValue(oFields) != null) - .Select(field => { - var objValue = field.GetValue(oFields, null); - var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue).ToString("dd.MM.yyyy") : objValue.ToString(); - return $"{field.Name}={value}"; - }) - .ToList(); -#else - return typeof(OptionalFields).GetProperties() - .Where(field => field.GetValue(oFields, null) != null) - .Select(field => { - var objValue = field.GetValue(oFields, null); - var value = field.PropertyType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); - return $"{field.Name}={value}"; - }) - .ToList(); -#endif + get => _drawerStatus; + set => _drawerStatus = value == null ? null : ValidateInput(value, "DrawerStatus", @"^.{1,2}$"); } - + private string? _kpp; /// - /// Takes all mandatory fields that are not null and returns their string represantion + /// KPP of the payee (Tax Registration Code; Text, up to 9 characters.) + /// КПП получателя платежа /// - /// A List of strings - private List GetMandatoryFieldsAsList() + public string? KPP { -#if NETSTANDARD1_3 - return typeof(MandatoryFields).GetRuntimeFields() - .Where(field => field.GetValue(mFields) != null) - .Select(field => { - var objValue = field.GetValue(mFields); - var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue).ToString("dd.MM.yyyy") : objValue.ToString(); - return $"{field.Name}={value}"; - }) - .ToList(); -#else - return typeof(MandatoryFields).GetFields() - .Where(field => field.GetValue(mFields) != null) - .Select(field => { - var objValue = field.GetValue(mFields); - var value = field.FieldType.Equals(typeof(DateTime?)) ? ((DateTime)objValue!).ToString("dd.MM.yyyy") : objValue!.ToString(); - return $"{field.Name}={value}"; - }) - .ToList(); -#endif + get => _kpp; + set => _kpp = value == null ? null : ValidateInput(value, "KPP", @"^.{1,9}$"); } + private string? _cbc; /// - /// Validates a string against a given Regex pattern. Returns input if it matches the Regex expression (=valid) or throws Exception in case there's a mismatch + /// CBC + /// КБК /// - /// String to be validated - /// Name/descriptor of the string to be validated - /// A regex pattern to be used for validation - /// An optional error text. If null, a standard error text is generated - /// Input value (in case it is valid) - private static string ValidateInput(string input, string fieldname, string pattern, string? errorText = null) + public string? CBC { - return ValidateInput(input, fieldname, new string[] { pattern }, errorText); + get => _cbc; + set => _cbc = value == null ? null : ValidateInput(value, "CBC", @"^.{1,20}$"); } + private string? _oktmo; /// - /// Validates a string against one or more given Regex patterns. Returns input if it matches all regex expressions (=valid) or throws Exception in case there's a mismatch + /// All-Russian classifier territories of municipal formations + /// Общероссийский классификатор территорий муниципальных образований /// - /// String to be validated - /// Name/descriptor of the string to be validated - /// An array of regex patterns to be used for validation - /// An optional error text. If null, a standard error text is generated - /// Input value (in case it is valid) - private static string ValidateInput(string input, string fieldname, string[] patterns, string? errorText = null) + public string? OKTMO { - if (input == null) - throw new RussiaPaymentOrderException($"The input for '{fieldname}' must not be null."); - foreach (var pattern in patterns) - { - if (!Regex.IsMatch(input, pattern)) - throw new RussiaPaymentOrderException(errorText ?? $"The input for '{fieldname}' ({input}) doesn't match the pattern {pattern}"); - } - return input; + get => _oktmo; + set => _oktmo = value == null ? null : ValidateInput(value, "OKTMO", @"^.{1,11}$"); } - private class MandatoryFields + private string? _paytReason; + /// + /// Basis of tax payment + /// Основание налогового платежа + /// + public string? PaytReason { - public string Name = null!; - public string PersonalAcc = null!; - public string BankName = null!; - public string BIC = null!; - public string CorrespAcc = null!; + get => _paytReason; + set => _paytReason = value == null ? null : ValidateInput(value, "PaytReason", @"^.{1,2}$"); } + private string? _taxPeriod; /// - /// Represents optional fields for the RussiaPaymentOrder. + /// Taxable period + /// Налоговый период /// - public class OptionalFields + public string? TaxPeriod { - private string? _sum; - /// - /// Payment amount, in kopecks (FTI’s Amount.) - /// Сумма платежа, в копейках - /// - public string? Sum - { - get { return _sum; } - set { _sum = value == null ? null : ValidateInput(value, "Sum", @"^\d{1,18}$"); } - } - - private string? _purpose; - /// - /// Payment name (purpose) - /// Наименование платежа (назначение) - /// - public string? Purpose - { - get { return _purpose; } - set { _purpose = value == null ? null : ValidateInput(value, "Purpose", @"^.{1,160}$"); } - } - - private string? _payeeInn; - /// - /// Payee's INN (Resident Tax Identification Number; Text, up to 12 characters.) - /// ИНН получателя платежа - /// - public string? PayeeINN - { - get { return _payeeInn; } - set { _payeeInn = value == null ? null : ValidateInput(value, "PayeeINN", @"^.{1,12}$"); } - } - - private string? _payerInn; - /// - /// Payer's INN (Resident Tax Identification Number; Text, up to 12 characters.) - /// ИНН плательщика - /// - public string? PayerINN - { - get { return _payerInn; } - set { _payerInn = value == null ? null : ValidateInput(value, "PayerINN", @"^.{1,12}$"); } - } - - private string? _drawerStatus; - /// - /// Status compiler payment document - /// Статус составителя платежного документа - /// - public string? DrawerStatus - { - get { return _drawerStatus; } - set { _drawerStatus = value == null ? null : ValidateInput(value, "DrawerStatus", @"^.{1,2}$"); } - } - - private string? _kpp; - /// - /// KPP of the payee (Tax Registration Code; Text, up to 9 characters.) - /// КПП получателя платежа - /// - public string? KPP - { - get { return _kpp; } - set { _kpp = value == null ? null : ValidateInput(value, "KPP", @"^.{1,9}$"); } - } - - private string? _cbc; - /// - /// CBC - /// КБК - /// - public string? CBC - { - get { return _cbc; } - set { _cbc = value == null ? null : ValidateInput(value, "CBC", @"^.{1,20}$"); } - } - - private string? _oktmo; - /// - /// All-Russian classifier territories of municipal formations - /// Общероссийский классификатор территорий муниципальных образований - /// - public string? OKTMO - { - get { return _oktmo; } - set { _oktmo = value == null ? null : ValidateInput(value, "OKTMO", @"^.{1,11}$"); } - } - - private string? _paytReason; - /// - /// Basis of tax payment - /// Основание налогового платежа - /// - public string? PaytReason - { - get { return _paytReason; } - set { _paytReason = value == null ? null : ValidateInput(value, "PaytReason", @"^.{1,2}$"); } - } - - private string? _taxPeriod; - /// - /// Taxable period - /// Налоговый период - /// - public string? TaxPeriod - { - get { return _taxPeriod; } - set { _taxPeriod = value == null ? null : ValidateInput(value, "ТaxPeriod", @"^.{1,10}$"); } - } - - private string? _docNo; - /// - /// Document number - /// Номер документа - /// - public string? DocNo - { - get { return _docNo; } - set { _docNo = value == null ? null : ValidateInput(value, "DocNo", @"^.{1,15}$"); } - } - - /// - /// Document date - /// Дата документа - /// - public DateTime? DocDate { get; set; } - - private string? _taxPaytKind; - /// - /// Payment type - /// Тип платежа - /// - public string? TaxPaytKind - { - get { return _taxPaytKind; } - set { _taxPaytKind = value == null ? null : ValidateInput(value, "TaxPaytKind", @"^.{1,2}$"); } - } - - /************************************************************************** - * The following fiels are no further specified in the standard - * document (https://sbqr.ru/standard/files/standart.pdf) thus there - * is no addition input validation implemented. - * **************************************************************************/ - - /// - /// Payer's surname - /// Фамилия плательщика - /// - public string? LastName { get; set; } - - /// - /// Payer's name - /// Имя плательщика - /// - public string? FirstName { get; set; } - - /// - /// Payer's patronymic - /// Отчество плательщика - /// - public string? MiddleName { get; set; } - - /// - /// Payer's address - /// Адрес плательщика - /// - public string? PayerAddress { get; set; } - - /// - /// Personal account of a budget recipient - /// Лицевой счет бюджетного получателя - /// - public string? PersonalAccount { get; set; } - - /// - /// Payment document index - /// Индекс платежного документа - /// - public string? DocIdx { get; set; } - - /// - /// Personal account number in the personalized accounting system in the Pension Fund of the Russian Federation - SNILS - /// № лицевого счета в системе персонифицированного учета в ПФР - СНИЛС - /// - public string? PensAcc { get; set; } - - /// - /// Number of contract - /// Номер договора - /// - public string? Contract { get; set; } - - /// - /// Personal account number of the payer in the organization (in the accounting system of the PU) - /// Номер лицевого счета плательщика в организации (в системе учета ПУ) - /// - public string? PersAcc { get; set; } - - /// - /// Apartment number - /// Номер квартиры - /// - public string? Flat { get; set; } - - /// - /// Phone number - /// Номер телефона - /// - public string? Phone { get; set; } - - /// - /// DUL payer type - /// Вид ДУЛ плательщика - /// - public string? PayerIdType { get; set; } - - /// - /// DUL number of the payer - /// Номер ДУЛ плательщика - /// - public string? PayerIdNum { get; set; } - - /// - /// FULL NAME. child / student - /// Ф.И.О. ребенка/учащегося - /// - public string? ChildFio { get; set; } - - /// - /// Date of birth - /// Дата рождения - /// - public DateTime? BirthDate { get; set; } - - /// - /// Due date / Invoice date - /// Срок платежа/дата выставления счета - /// - public string? PaymTerm { get; set; } - - /// - /// Payment period - /// Период оплаты - /// - public string? PaymPeriod { get; set; } - - /// - /// Payment type - /// Вид платежа - /// - public string? Category { get; set; } - - /// - /// Service code / meter name - /// Код услуги/название прибора учета - /// - public string? ServiceName { get; set; } - - /// - /// Metering device number - /// Номер прибора учета - /// - public string? CounterId { get; set; } - - /// - /// Meter reading - /// Показание прибора учета - /// - public string? CounterVal { get; set; } - - /// - /// Notification, accrual, account number - /// Номер извещения, начисления, счета - /// - public string? QuittId { get; set; } - - /// - /// Date of notification / accrual / invoice / resolution (for traffic police) - /// Дата извещения/начисления/счета/постановления (для ГИБДД) - /// - public DateTime? QuittDate { get; set; } - - /// - /// Institution number (educational, medical) - /// Номер учреждения (образовательного, медицинского) - /// - public string? InstNum { get; set; } - - /// - /// Kindergarten / school class number - /// Номер группы детсада/класса школы - /// - public string? ClassNum { get; set; } - - /// - /// Full name of the teacher, specialist providing the service - /// ФИО преподавателя, специалиста, оказывающего услугу - /// - public string? SpecFio { get; set; } - - /// - /// Insurance / additional service amount / Penalty amount (in kopecks) - /// Сумма страховки/дополнительной услуги/Сумма пени (в копейках) - /// - public string? AddAmount { get; set; } - - /// - /// Resolution number (for traffic police) - /// Номер постановления (для ГИБДД) - /// - public string? RuleId { get; set; } - - /// - /// Enforcement Proceedings Number - /// Номер исполнительного производства - /// - public string? ExecId { get; set; } - - /// - /// Type of payment code (for example, for payments to Rosreestr) - /// Код вида платежа (например, для платежей в адрес Росреестра) - /// - public string? RegType { get; set; } - - /// - /// Unique accrual identifier - /// Уникальный идентификатор начисления - /// - public string? UIN { get; set; } - - /// - /// The technical code recommended by the service provider. Maybe used by the receiving organization to call the appropriate processing IT system. - /// Технический код, рекомендуемый для заполнения поставщиком услуг. Может использоваться принимающей организацией для вызова соответствующей обрабатывающей ИТ-системы. - /// - public TechCode? TechCode { get; set; } + get => _taxPeriod; + set => _taxPeriod = value == null ? null : ValidateInput(value, "ТaxPeriod", @"^.{1,10}$"); } - /// - /// (List of values of the technical code of the payment) - /// Перечень значений технического кода платежа + private string? _docNo; + /// + /// Document number + /// Номер документа /// - public enum TechCode + public string? DocNo { - Мобильная_связь_стационарный_телефон = 01, - Коммунальные_услуги_ЖКХAFN = 02, - ГИБДД_налоги_пошлины_бюджетные_платежи = 03, - Охранные_услуги = 04, - Услуги_оказываемые_УФМС = 05, - ПФР = 06, - Погашение_кредитов = 07, - Образовательные_учреждения = 08, - Интернет_и_ТВ = 09, - Электронные_деньги = 10, - Отдых_и_путешествия = 11, - Инвестиции_и_страхование = 12, - Спорт_и_здоровье = 13, - Благотворительные_и_общественные_организации = 14, - Прочие_услуги = 15 + get => _docNo; + set => _docNo = value == null ? null : ValidateInput(value, "DocNo", @"^.{1,15}$"); } /// - /// Specifies character sets for encoding the RussiaPaymentOrder payload. + /// Document date + /// Дата документа + /// + public DateTime? DocDate { get; set; } + + private string? _taxPaytKind; + /// + /// Payment type + /// Тип платежа /// - public enum CharacterSets + public string? TaxPaytKind { - /// - /// Windows-1251 encoding. - /// - windows_1251 = 1, - - /// - /// UTF-8 encoding. - /// - utf_8 = 2, - - /// - /// KOI8-R encoding. - /// - koi8_r = 3 + get => _taxPaytKind; + set => _taxPaytKind = value == null ? null : ValidateInput(value, "TaxPaytKind", @"^.{1,2}$"); } + /************************************************************************** + * The following fiels are no further specified in the standard + * document (https://sbqr.ru/standard/files/standart.pdf) thus there + * is no addition input validation implemented. + * **************************************************************************/ + + /// + /// Payer's surname + /// Фамилия плательщика + /// + public string? LastName { get; set; } + + /// + /// Payer's name + /// Имя плательщика + /// + public string? FirstName { get; set; } + + /// + /// Payer's patronymic + /// Отчество плательщика + /// + public string? MiddleName { get; set; } + + /// + /// Payer's address + /// Адрес плательщика + /// + public string? PayerAddress { get; set; } + /// - /// Represents errors that occur during the generation of a RussiaPaymentOrder payload. + /// Personal account of a budget recipient + /// Лицевой счет бюджетного получателя /// - public class RussiaPaymentOrderException : Exception + public string? PersonalAccount { get; set; } + + /// + /// Payment document index + /// Индекс платежного документа + /// + public string? DocIdx { get; set; } + + /// + /// Personal account number in the personalized accounting system in the Pension Fund of the Russian Federation - SNILS + /// № лицевого счета в системе персонифицированного учета в ПФР - СНИЛС + /// + public string? PensAcc { get; set; } + + /// + /// Number of contract + /// Номер договора + /// + public string? Contract { get; set; } + + /// + /// Personal account number of the payer in the organization (in the accounting system of the PU) + /// Номер лицевого счета плательщика в организации (в системе учета ПУ) + /// + public string? PersAcc { get; set; } + + /// + /// Apartment number + /// Номер квартиры + /// + public string? Flat { get; set; } + + /// + /// Phone number + /// Номер телефона + /// + public string? Phone { get; set; } + + /// + /// DUL payer type + /// Вид ДУЛ плательщика + /// + public string? PayerIdType { get; set; } + + /// + /// DUL number of the payer + /// Номер ДУЛ плательщика + /// + public string? PayerIdNum { get; set; } + + /// + /// FULL NAME. child / student + /// Ф.И.О. ребенка/учащегося + /// + public string? ChildFio { get; set; } + + /// + /// Date of birth + /// Дата рождения + /// + public DateTime? BirthDate { get; set; } + + /// + /// Due date / Invoice date + /// Срок платежа/дата выставления счета + /// + public string? PaymTerm { get; set; } + + /// + /// Payment period + /// Период оплаты + /// + public string? PaymPeriod { get; set; } + + /// + /// Payment type + /// Вид платежа + /// + public string? Category { get; set; } + + /// + /// Service code / meter name + /// Код услуги/название прибора учета + /// + public string? ServiceName { get; set; } + + /// + /// Metering device number + /// Номер прибора учета + /// + public string? CounterId { get; set; } + + /// + /// Meter reading + /// Показание прибора учета + /// + public string? CounterVal { get; set; } + + /// + /// Notification, accrual, account number + /// Номер извещения, начисления, счета + /// + public string? QuittId { get; set; } + + /// + /// Date of notification / accrual / invoice / resolution (for traffic police) + /// Дата извещения/начисления/счета/постановления (для ГИБДД) + /// + public DateTime? QuittDate { get; set; } + + /// + /// Institution number (educational, medical) + /// Номер учреждения (образовательного, медицинского) + /// + public string? InstNum { get; set; } + + /// + /// Kindergarten / school class number + /// Номер группы детсада/класса школы + /// + public string? ClassNum { get; set; } + + /// + /// Full name of the teacher, specialist providing the service + /// ФИО преподавателя, специалиста, оказывающего услугу + /// + public string? SpecFio { get; set; } + + /// + /// Insurance / additional service amount / Penalty amount (in kopecks) + /// Сумма страховки/дополнительной услуги/Сумма пени (в копейках) + /// + public string? AddAmount { get; set; } + + /// + /// Resolution number (for traffic police) + /// Номер постановления (для ГИБДД) + /// + public string? RuleId { get; set; } + + /// + /// Enforcement Proceedings Number + /// Номер исполнительного производства + /// + public string? ExecId { get; set; } + + /// + /// Type of payment code (for example, for payments to Rosreestr) + /// Код вида платежа (например, для платежей в адрес Росреестра) + /// + public string? RegType { get; set; } + + /// + /// Unique accrual identifier + /// Уникальный идентификатор начисления + /// + public string? UIN { get; set; } + + /// + /// The technical code recommended by the service provider. Maybe used by the receiving organization to call the appropriate processing IT system. + /// Технический код, рекомендуемый для заполнения поставщиком услуг. Может использоваться принимающей организацией для вызова соответствующей обрабатывающей ИТ-системы. + /// + public TechCode? TechCode { get; set; } + } + + /// + /// (List of values of the technical code of the payment) + /// Перечень значений технического кода платежа + /// + public enum TechCode + { + Мобильная_связь_стационарный_телефон = 01, + Коммунальные_услуги_ЖКХAFN = 02, + ГИБДД_налоги_пошлины_бюджетные_платежи = 03, + Охранные_услуги = 04, + Услуги_оказываемые_УФМС = 05, + ПФР = 06, + Погашение_кредитов = 07, + Образовательные_учреждения = 08, + Интернет_и_ТВ = 09, + Электронные_деньги = 10, + Отдых_и_путешествия = 11, + Инвестиции_и_страхование = 12, + Спорт_и_здоровье = 13, + Благотворительные_и_общественные_организации = 14, + Прочие_услуги = 15 + } + + /// + /// Specifies character sets for encoding the RussiaPaymentOrder payload. + /// + public enum CharacterSets + { + /// + /// Windows-1251 encoding. + /// + windows_1251 = 1, + + /// + /// UTF-8 encoding. + /// + utf_8 = 2, + + /// + /// KOI8-R encoding. + /// + koi8_r = 3 + } + + /// + /// Represents errors that occur during the generation of a RussiaPaymentOrder payload. + /// + public class RussiaPaymentOrderException : Exception + { + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public RussiaPaymentOrderException(string message) + : base(message) { - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public RussiaPaymentOrderException(string message) - : base(message) - { - } } - } + } } diff --git a/QRCoder/PayloadGenerator/SMS.cs b/QRCoder/PayloadGenerator/SMS.cs index b09256ef..62af2849 100644 --- a/QRCoder/PayloadGenerator/SMS.cs +++ b/QRCoder/PayloadGenerator/SMS.cs @@ -1,88 +1,71 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates an SMS payload. + /// + public class SMS : Payload { + private readonly string _number, _subject; + private readonly SMSEncoding _encoding; + /// - /// Generates an SMS payload. + /// Creates an SMS payload without text. /// - public class SMS : Payload + /// Receiver phone number. + /// Encoding type. + public SMS(string number, SMSEncoding encoding = SMSEncoding.SMS) { - private readonly string number, subject; - private readonly SMSEncoding encoding; + _number = number; + _subject = string.Empty; + _encoding = encoding; + } - /// - /// Creates an SMS payload without text. - /// - /// Receiver phone number. - /// Encoding type. - public SMS(string number, SMSEncoding encoding = SMSEncoding.SMS) - { - this.number = number; - this.subject = string.Empty; - this.encoding = encoding; - } + /// + /// Creates an SMS payload with text (subject). + /// + /// Receiver phone number. + /// Text of the SMS. + /// Encoding type. + public SMS(string number, string subject, SMSEncoding encoding = SMSEncoding.SMS) + { + _number = number; + _subject = subject; + _encoding = encoding; + } + /// + /// Returns the SMS payload as a string. + /// + /// The SMS payload as a string. + public override string ToString() => _encoding switch + { + SMSEncoding.SMS => $"sms:{_number}{(string.IsNullOrEmpty(_subject) ? string.Empty : $"?body={Uri.EscapeDataString(_subject)}")}", + SMSEncoding.SMS_iOS => $"sms:{_number}{(string.IsNullOrEmpty(_subject) ? string.Empty : $";body={Uri.EscapeDataString(_subject)}")}", + SMSEncoding.SMSTO => $"SMSTO:{_number}:{_subject}", + _ => string.Empty, + }; + + /// + /// Specifies the encoding type for the SMS payload. + /// + public enum SMSEncoding + { /// - /// Creates an SMS payload with text (subject). + /// Standard SMS encoding. /// - /// Receiver phone number. - /// Text of the SMS. - /// Encoding type. - public SMS(string number, string subject, SMSEncoding encoding = SMSEncoding.SMS) - { - this.number = number; - this.subject = subject; - this.encoding = encoding; - } - + SMS, /// - /// Returns the SMS payload as a string. + /// SMSTO encoding. /// - /// The SMS payload as a string. - public override string ToString() - { - var returnVal = string.Empty; - switch (this.encoding) - { - case SMSEncoding.SMS: - var queryString = string.Empty; - if (!string.IsNullOrEmpty(this.subject)) - queryString = $"?body={Uri.EscapeDataString(this.subject)}"; - returnVal = $"sms:{this.number}{queryString}"; - break; - case SMSEncoding.SMS_iOS: - var queryStringiOS = string.Empty; - if (!string.IsNullOrEmpty(this.subject)) - queryStringiOS = $";body={Uri.EscapeDataString(this.subject)}"; - returnVal = $"sms:{this.number}{queryStringiOS}"; - break; - case SMSEncoding.SMSTO: - returnVal = $"SMSTO:{this.number}:{this.subject}"; - break; - } - return returnVal; - } - + SMSTO, /// - /// Specifies the encoding type for the SMS payload. + /// SMS encoding for iOS. /// - public enum SMSEncoding - { - /// - /// Standard SMS encoding. - /// - SMS, - /// - /// SMSTO encoding. - /// - SMSTO, - /// - /// SMS encoding for iOS. - /// - SMS_iOS - } + SMS_iOS } } } diff --git a/QRCoder/PayloadGenerator/ShadowSocksConfig.cs b/QRCoder/PayloadGenerator/ShadowSocksConfig.cs index 91ed5350..67b2821f 100644 --- a/QRCoder/PayloadGenerator/ShadowSocksConfig.cs +++ b/QRCoder/PayloadGenerator/ShadowSocksConfig.cs @@ -1,274 +1,273 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a ShadowSocks proxy config payload. + /// + public class ShadowSocksConfig : Payload { - /// - /// Generates a ShadowSocks proxy config payload. - /// - public class ShadowSocksConfig : Payload - { - private readonly string hostname, password, methodStr; - private readonly string? tag, parameter; - private readonly Method method; - private readonly int port; - private Dictionary encryptionTexts = new Dictionary() { - { "Chacha20IetfPoly1305", "chacha20-ietf-poly1305" }, - { "Aes128Gcm", "aes-128-gcm" }, - { "Aes192Gcm", "aes-192-gcm" }, - { "Aes256Gcm", "aes-256-gcm" }, + private readonly string _hostname, _password, _methodStr; + private readonly string? _tag, _parameter; + private readonly Method _method; + private readonly int _port; + private readonly Dictionary _encryptionTexts = new Dictionary() { + { "Chacha20IetfPoly1305", "chacha20-ietf-poly1305" }, + { "Aes128Gcm", "aes-128-gcm" }, + { "Aes192Gcm", "aes-192-gcm" }, + { "Aes256Gcm", "aes-256-gcm" }, - { "XChacha20IetfPoly1305", "xchacha20-ietf-poly1305" }, + { "XChacha20IetfPoly1305", "xchacha20-ietf-poly1305" }, - { "Aes128Cfb", "aes-128-cfb" }, - { "Aes192Cfb", "aes-192-cfb" }, - { "Aes256Cfb", "aes-256-cfb" }, - { "Aes128Ctr", "aes-128-ctr" }, - { "Aes192Ctr", "aes-192-ctr" }, - { "Aes256Ctr", "aes-256-ctr" }, - { "Camellia128Cfb", "camellia-128-cfb" }, - { "Camellia192Cfb", "camellia-192-cfb" }, - { "Camellia256Cfb", "camellia-256-cfb" }, - { "Chacha20Ietf", "chacha20-ietf" }, + { "Aes128Cfb", "aes-128-cfb" }, + { "Aes192Cfb", "aes-192-cfb" }, + { "Aes256Cfb", "aes-256-cfb" }, + { "Aes128Ctr", "aes-128-ctr" }, + { "Aes192Ctr", "aes-192-ctr" }, + { "Aes256Ctr", "aes-256-ctr" }, + { "Camellia128Cfb", "camellia-128-cfb" }, + { "Camellia192Cfb", "camellia-192-cfb" }, + { "Camellia256Cfb", "camellia-256-cfb" }, + { "Chacha20Ietf", "chacha20-ietf" }, - { "Aes256Cb", "aes-256-cfb" }, + { "Aes256Cb", "aes-256-cfb" }, - { "Aes128Ofb", "aes-128-ofb" }, - { "Aes192Ofb", "aes-192-ofb" }, - { "Aes256Ofb", "aes-256-ofb" }, - { "Aes128Cfb1", "aes-128-cfb1" }, - { "Aes192Cfb1", "aes-192-cfb1" }, - { "Aes256Cfb1", "aes-256-cfb1" }, - { "Aes128Cfb8", "aes-128-cfb8" }, - { "Aes192Cfb8", "aes-192-cfb8" }, - { "Aes256Cfb8", "aes-256-cfb8" }, + { "Aes128Ofb", "aes-128-ofb" }, + { "Aes192Ofb", "aes-192-ofb" }, + { "Aes256Ofb", "aes-256-ofb" }, + { "Aes128Cfb1", "aes-128-cfb1" }, + { "Aes192Cfb1", "aes-192-cfb1" }, + { "Aes256Cfb1", "aes-256-cfb1" }, + { "Aes128Cfb8", "aes-128-cfb8" }, + { "Aes192Cfb8", "aes-192-cfb8" }, + { "Aes256Cfb8", "aes-256-cfb8" }, - { "Chacha20", "chacha20" }, - { "BfCfb", "bf-cfb" }, - { "Rc4Md5", "rc4-md5" }, - { "Salsa20", "salsa20" }, + { "Chacha20", "chacha20" }, + { "BfCfb", "bf-cfb" }, + { "Rc4Md5", "rc4-md5" }, + { "Salsa20", "salsa20" }, - { "DesCfb", "des-cfb" }, - { "IdeaCfb", "idea-cfb" }, - { "Rc2Cfb", "rc2-cfb" }, - { "Cast5Cfb", "cast5-cfb" }, - { "Salsa20Ctr", "salsa20-ctr" }, - { "Rc4", "rc4" }, - { "SeedCfb", "seed-cfb" }, - { "Table", "table" } - }; + { "DesCfb", "des-cfb" }, + { "IdeaCfb", "idea-cfb" }, + { "Rc2Cfb", "rc2-cfb" }, + { "Cast5Cfb", "cast5-cfb" }, + { "Salsa20Ctr", "salsa20-ctr" }, + { "Rc4", "rc4" }, + { "SeedCfb", "seed-cfb" }, + { "Table", "table" } + }; - /// - /// Generates a ShadowSocks proxy config payload. - /// - /// Hostname of the ShadowSocks proxy - /// Port of the ShadowSocks proxy - /// Password of the SS proxy - /// Encryption type - /// Optional tag line - public ShadowSocksConfig(string hostname, int port, string password, Method method, string? tag = null) : - this(hostname, port, password, method, null, tag) - { } + /// + /// Generates a ShadowSocks proxy config payload. + /// + /// Hostname of the ShadowSocks proxy + /// Port of the ShadowSocks proxy + /// Password of the SS proxy + /// Encryption type + /// Optional tag line + public ShadowSocksConfig(string hostname, int port, string password, Method method, string? tag = null) : + this(hostname, port, password, method, null, tag) + { } - /// - /// Generates a ShadowSocks proxy config payload with plugin options. - /// - /// Hostname of the ShadowSocks proxy - /// Port of the ShadowSocks proxy - /// Password of the SS proxy - /// Encryption type - /// Plugin name - /// Plugin option - /// Optional tag line - public ShadowSocksConfig(string hostname, int port, string password, Method method, string plugin, string? pluginOption, string? tag = null) : - this(hostname, port, password, method, new Dictionary - { - ["plugin"] = plugin + ( - string.IsNullOrEmpty(pluginOption) - ? "" - : $";{pluginOption}" - ) - }, tag) - { } - private Dictionary UrlEncodeTable = new Dictionary + /// + /// Generates a ShadowSocks proxy config payload with plugin options. + /// + /// Hostname of the ShadowSocks proxy + /// Port of the ShadowSocks proxy + /// Password of the SS proxy + /// Encryption type + /// Plugin name + /// Plugin option + /// Optional tag line + public ShadowSocksConfig(string hostname, int port, string password, Method method, string plugin, string? pluginOption, string? tag = null) : + this(hostname, port, password, method, new Dictionary { - [" "] = "+", - ["\0"] = "%00", - ["\t"] = "%09", - ["\n"] = "%0a", - ["\r"] = "%0d", - ["\""] = "%22", - ["#"] = "%23", - ["$"] = "%24", - ["%"] = "%25", - ["&"] = "%26", - ["'"] = "%27", - ["+"] = "%2b", - [","] = "%2c", - ["/"] = "%2f", - [":"] = "%3a", - [";"] = "%3b", - ["<"] = "%3c", - ["="] = "%3d", - [">"] = "%3e", - ["?"] = "%3f", - ["@"] = "%40", - ["["] = "%5b", - ["\\"] = "%5c", - ["]"] = "%5d", - ["^"] = "%5e", - ["`"] = "%60", - ["{"] = "%7b", - ["|"] = "%7c", - ["}"] = "%7d", - ["~"] = "%7e", - }; + ["plugin"] = plugin + ( + string.IsNullOrEmpty(pluginOption) + ? "" + : $";{pluginOption}" + ) + }, tag) + { } + private readonly Dictionary _urlEncodeTable = new Dictionary + { + [" "] = "+", + ["\0"] = "%00", + ["\t"] = "%09", + ["\n"] = "%0a", + ["\r"] = "%0d", + ["\""] = "%22", + ["#"] = "%23", + ["$"] = "%24", + ["%"] = "%25", + ["&"] = "%26", + ["'"] = "%27", + ["+"] = "%2b", + [","] = "%2c", + ["/"] = "%2f", + [":"] = "%3a", + [";"] = "%3b", + ["<"] = "%3c", + ["="] = "%3d", + [">"] = "%3e", + ["?"] = "%3f", + ["@"] = "%40", + ["["] = "%5b", + ["\\"] = "%5c", + ["]"] = "%5d", + ["^"] = "%5e", + ["`"] = "%60", + ["{"] = "%7b", + ["|"] = "%7c", + ["}"] = "%7d", + ["~"] = "%7e", + }; - private string UrlEncode(string i) + private string UrlEncode(string i) + { + string j = i; + foreach (var kv in _urlEncodeTable) { - string j = i; - foreach (var kv in UrlEncodeTable) - { - j = j.Replace(kv.Key, kv.Value); - } - return j; + j = j.Replace(kv.Key, kv.Value); } + return j; + } - /// - /// Generates a ShadowSocks proxy config payload with additional parameters. - /// - /// Hostname of the ShadowSocks proxy - /// Port of the ShadowSocks proxy - /// Password of the SS proxy - /// Encryption type - /// Additional parameters - /// Optional tag line - public ShadowSocksConfig(string hostname, int port, string password, Method method, Dictionary? parameters, string? tag = null) - { - this.hostname = Uri.CheckHostName(hostname) == UriHostNameType.IPv6 - ? $"[{hostname}]" - : hostname; - if (port < 1 || port > 65535) - throw new ShadowSocksConfigException("Value of 'port' must be within 0 and 65535."); - this.port = port; - this.password = password; - this.method = method; - this.methodStr = encryptionTexts[method.ToString()]; - this.tag = tag; + /// + /// Generates a ShadowSocks proxy config payload with additional parameters. + /// + /// Hostname of the ShadowSocks proxy + /// Port of the ShadowSocks proxy + /// Password of the SS proxy + /// Encryption type + /// Additional parameters + /// Optional tag line + public ShadowSocksConfig(string hostname, int port, string password, Method method, Dictionary? parameters, string? tag = null) + { + _hostname = Uri.CheckHostName(hostname) == UriHostNameType.IPv6 + ? $"[{hostname}]" + : hostname; + if (port < 1 || port > 65535) + throw new ShadowSocksConfigException("Value of 'port' must be within 0 and 65535."); + _port = port; + _password = password; + _method = method; + _methodStr = _encryptionTexts[method.ToString()]; + _tag = tag; + + if (parameters != null) + _parameter = + string.Join("&", + parameters.Select( + kv => $"{UrlEncode(kv.Key)}={UrlEncode(kv.Value)}" + ).ToArray()); + } - if (parameters != null) - this.parameter = - string.Join("&", - parameters.Select( - kv => $"{UrlEncode(kv.Key)}={UrlEncode(kv.Value)}" - ).ToArray()); + /// + /// Converts the ShadowSocks config payload to a string. + /// + /// A string representation of the ShadowSocks config payload. + public override string ToString() + { + if (string.IsNullOrEmpty(_parameter)) + { + var connectionString = $"{_methodStr}:{_password}@{_hostname}:{_port}"; + var connectionStringEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(connectionString)); + return $"ss://{connectionStringEncoded}{(!string.IsNullOrEmpty(_tag) ? $"#{_tag}" : string.Empty)}"; } + var authString = $"{_methodStr}:{_password}"; + var authStringEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString)) + .Replace('+', '-') + .Replace('/', '_') + .TrimEnd('='); + return $"ss://{authStringEncoded}@{_hostname}:{_port}/?{_parameter}{(!string.IsNullOrEmpty(_tag) ? $"#{_tag}" : string.Empty)}"; + } + /// + /// Specifies the encryption methods used by ShadowSocks. + /// + public enum Method + { + // AEAD + Chacha20IetfPoly1305, + Aes128Gcm, + Aes192Gcm, + Aes256Gcm, + // AEAD, not standard + XChacha20IetfPoly1305, + // Stream cipher + Aes128Cfb, + Aes192Cfb, + Aes256Cfb, + Aes128Ctr, + Aes192Ctr, + Aes256Ctr, + Camellia128Cfb, + Camellia192Cfb, + Camellia256Cfb, + Chacha20Ietf, + // alias of Aes256Cfb + Aes256Cb, + // Stream cipher, not standard + Aes128Ofb, + Aes192Ofb, + Aes256Ofb, + Aes128Cfb1, + Aes192Cfb1, + Aes256Cfb1, + Aes128Cfb8, + Aes192Cfb8, + Aes256Cfb8, + // Stream cipher, deprecated + Chacha20, + BfCfb, + Rc4Md5, + Salsa20, + // Not standard and not in active use + DesCfb, + IdeaCfb, + Rc2Cfb, + Cast5Cfb, + Salsa20Ctr, + Rc4, + SeedCfb, + Table + } + + /// + /// Represents errors that occur during the generation of a ShadowSocksConfig payload. + /// + public class ShadowSocksConfigException : Exception + { /// - /// Converts the ShadowSocks config payload to a string. + /// Initializes a new instance of the class. /// - /// A string representation of the ShadowSocks config payload. - public override string ToString() + public ShadowSocksConfigException() { - if (string.IsNullOrEmpty(parameter)) - { - var connectionString = $"{methodStr}:{password}@{hostname}:{port}"; - var connectionStringEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(connectionString)); - return $"ss://{connectionStringEncoded}{(!string.IsNullOrEmpty(tag) ? $"#{tag}" : string.Empty)}"; - } - var authString = $"{methodStr}:{password}"; - var authStringEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString)) - .Replace('+', '-') - .Replace('/', '_') - .TrimEnd('='); - return $"ss://{authStringEncoded}@{hostname}:{port}/?{parameter}{(!string.IsNullOrEmpty(tag) ? $"#{tag}" : string.Empty)}"; } /// - /// Specifies the encryption methods used by ShadowSocks. + /// Initializes a new instance of the class with a specified error message. /// - public enum Method + /// The message that describes the error. + public ShadowSocksConfigException(string message) + : base(message) { - // AEAD - Chacha20IetfPoly1305, - Aes128Gcm, - Aes192Gcm, - Aes256Gcm, - // AEAD, not standard - XChacha20IetfPoly1305, - // Stream cipher - Aes128Cfb, - Aes192Cfb, - Aes256Cfb, - Aes128Ctr, - Aes192Ctr, - Aes256Ctr, - Camellia128Cfb, - Camellia192Cfb, - Camellia256Cfb, - Chacha20Ietf, - // alias of Aes256Cfb - Aes256Cb, - // Stream cipher, not standard - Aes128Ofb, - Aes192Ofb, - Aes256Ofb, - Aes128Cfb1, - Aes192Cfb1, - Aes256Cfb1, - Aes128Cfb8, - Aes192Cfb8, - Aes256Cfb8, - // Stream cipher, deprecated - Chacha20, - BfCfb, - Rc4Md5, - Salsa20, - // Not standard and not in active use - DesCfb, - IdeaCfb, - Rc2Cfb, - Cast5Cfb, - Salsa20Ctr, - Rc4, - SeedCfb, - Table } /// - /// Represents errors that occur during the generation of a ShadowSocksConfig payload. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class ShadowSocksConfigException : Exception + /// The message that describes the error. + /// The exception that is the cause of the current exception. + public ShadowSocksConfigException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public ShadowSocksConfigException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public ShadowSocksConfigException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The message that describes the error. - /// The exception that is the cause of the current exception. - public ShadowSocksConfigException(string message, Exception inner) - : base(message, inner) - { - } } } } diff --git a/QRCoder/PayloadGenerator/SkypeCall.cs b/QRCoder/PayloadGenerator/SkypeCall.cs index 554e2d2c..1df3d91d 100644 --- a/QRCoder/PayloadGenerator/SkypeCall.cs +++ b/QRCoder/PayloadGenerator/SkypeCall.cs @@ -1,31 +1,27 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a Skype call payload + /// + public class SkypeCall : Payload { + private readonly string _skypeUsername; + /// - /// Generates a Skype call payload + /// Initializes a new instance of the class. /// - public class SkypeCall : Payload + /// Skype username which will be called + public SkypeCall(string skypeUsername) { - private readonly string skypeUsername; - - /// - /// Initializes a new instance of the class. - /// - /// Skype username which will be called - public SkypeCall(string skypeUsername) - { - this.skypeUsername = skypeUsername; - } - - /// - /// Converts the Skype call payload to a string. - /// - /// A string representation of the Skype call payload. - public override string ToString() - { - return $"skype:{this.skypeUsername}?call"; - } + _skypeUsername = skypeUsername; } + + /// + /// Converts the Skype call payload to a string. + /// + /// A string representation of the Skype call payload. + public override string ToString() => $"skype:{_skypeUsername}?call"; } } diff --git a/QRCoder/PayloadGenerator/SlovenianUpnQr.cs b/QRCoder/PayloadGenerator/SlovenianUpnQr.cs index d9831180..b34700bc 100644 --- a/QRCoder/PayloadGenerator/SlovenianUpnQr.cs +++ b/QRCoder/PayloadGenerator/SlovenianUpnQr.cs @@ -1,169 +1,166 @@ -using System; +using System; using System.Text; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a Slovenian UPN QR payment payload. + /// + public class SlovenianUpnQr : Payload { - /// - /// Generates a Slovenian UPN QR payment payload. - /// - public class SlovenianUpnQr : Payload - { - //Keep in mind, that the ECC level has to be set to "M", version to 15 and ECI to EciMode.Iso8859_2 when generating a SlovenianUpnQr! - //SlovenianUpnQr specification: https://www.upn-qr.si/uploads/files/NavodilaZaProgramerjeUPNQR.pdf + //Keep in mind, that the ECC level has to be set to "M", version to 15 and ECI to EciMode.Iso8859_2 when generating a SlovenianUpnQr! + //SlovenianUpnQr specification: https://www.upn-qr.si/uploads/files/NavodilaZaProgramerjeUPNQR.pdf - private string _payerName = ""; - private string _payerAddress = ""; - private string _payerPlace = ""; - private string _amount = ""; - private string _code = ""; - private string _purpose = ""; - private string _deadLine = ""; - private string _recipientIban = ""; - private string _recipientName = ""; - private string _recipientAddress = ""; - private string _recipientPlace = ""; - private string _recipientSiModel = ""; - private string _recipientSiReference = ""; + private readonly string _payerName = ""; + private readonly string _payerAddress = ""; + private readonly string _payerPlace = ""; + private readonly string _amount = ""; + private readonly string _code = ""; + private readonly string _purpose = ""; + private readonly string _deadLine = ""; + private readonly string _recipientIban = ""; + private readonly string _recipientName = ""; + private readonly string _recipientAddress = ""; + private readonly string _recipientPlace = ""; + private readonly string _recipientSiModel = ""; + private readonly string _recipientSiReference = ""; - /// - /// Gets the version of the QR code, which is 15 for Slovenian UPN QR. - /// - public override int Version { get { return 15; } } + /// + /// Gets the version of the QR code, which is 15 for Slovenian UPN QR. + /// + public override int Version => 15; - /// - /// Gets the error correction level of the QR code, which is M for Slovenian UPN QR. - /// - public override QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } } + /// + /// Gets the error correction level of the QR code, which is M for Slovenian UPN QR. + /// + public override QRCodeGenerator.ECCLevel EccLevel => QRCodeGenerator.ECCLevel.M; - /// - /// Gets the ECI mode of the QR code, which is Iso8859_2 for Slovenian UPN QR. - /// - public override QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Iso8859_2; } } + /// + /// Gets the ECI mode of the QR code, which is Iso8859_2 for Slovenian UPN QR. + /// + public override QRCodeGenerator.EciMode EciMode => QRCodeGenerator.EciMode.Iso8859_2; - /// - /// Limits the length of a string to a specified maximum length. - /// - /// The string to limit. - /// The maximum length of the string. - /// The limited string. - private string LimitLength(string value, int maxLength) - { - return (value.Length <= maxLength) ? value : value.Substring(0, maxLength); - } + /// + /// Limits the length of a string to a specified maximum length. + /// + /// The string to limit. + /// The maximum length of the string. + /// The limited string. + private string LimitLength(string value, int maxLength) + => (value.Length <= maxLength) ? value : value.Substring(0, maxLength); - /// - /// Initializes a new instance of the class. - /// - /// The name of the payer. - /// The address of the payer. - /// The place of the payer. - /// The name of the recipient. - /// The address of the recipient. - /// The place of the recipient. - /// The IBAN of the recipient. - /// The description of the payment. - /// The amount of the payment. - /// The SI model of the recipient. - /// The SI reference of the recipient. - /// The code of the payment. - public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, string recipientName, string recipientAddress, string recipientPlace, string recipientIban, string description, double amount, string recipientSiModel = "SI00", string recipientSiReference = "", string code = "OTHR") : - this(payerName, payerAddress, payerPlace, recipientName, recipientAddress, recipientPlace, recipientIban, description, amount, null, recipientSiModel, recipientSiReference, code) - { } + /// + /// Initializes a new instance of the class. + /// + /// The name of the payer. + /// The address of the payer. + /// The place of the payer. + /// The name of the recipient. + /// The address of the recipient. + /// The place of the recipient. + /// The IBAN of the recipient. + /// The description of the payment. + /// The amount of the payment. + /// The SI model of the recipient. + /// The SI reference of the recipient. + /// The code of the payment. + public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, string recipientName, string recipientAddress, string recipientPlace, string recipientIban, string description, double amount, string recipientSiModel = "SI00", string recipientSiReference = "", string code = "OTHR") : + this(payerName, payerAddress, payerPlace, recipientName, recipientAddress, recipientPlace, recipientIban, description, amount, null, recipientSiModel, recipientSiReference, code) + { } - /// - /// Initializes a new instance of the class with a deadline. - /// - /// The name of the payer. - /// The address of the payer. - /// The place of the payer. - /// The name of the recipient. - /// The address of the recipient. - /// The place of the recipient. - /// The IBAN of the recipient. - /// The description of the payment. - /// The amount of the payment. - /// The deadline for the payment. - /// The SI model of the recipient. - /// The SI reference of the recipient. - /// The code of the payment. - public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, string recipientName, string recipientAddress, string recipientPlace, string recipientIban, string description, double amount, DateTime? deadline, string recipientSiModel = "SI99", string recipientSiReference = "", string code = "OTHR") - { - _payerName = LimitLength(payerName.Trim(), 33); - _payerAddress = LimitLength(payerAddress.Trim(), 33); - _payerPlace = LimitLength(payerPlace.Trim(), 33); - _amount = FormatAmount(amount); - _code = LimitLength(code.Trim().ToUpper(), 4); - _purpose = LimitLength(description.Trim(), 42); - _deadLine = (deadline == null) ? "" : deadline.Value.ToString("dd.MM.yyyy"); - _recipientIban = LimitLength(recipientIban.Trim(), 34); - _recipientName = LimitLength(recipientName.Trim(), 33); - _recipientAddress = LimitLength(recipientAddress.Trim(), 33); - _recipientPlace = LimitLength(recipientPlace.Trim(), 33); - _recipientSiModel = LimitLength(recipientSiModel.Trim().ToUpper(), 4); - _recipientSiReference = LimitLength(recipientSiReference.Trim(), 22); - } + /// + /// Initializes a new instance of the class with a deadline. + /// + /// The name of the payer. + /// The address of the payer. + /// The place of the payer. + /// The name of the recipient. + /// The address of the recipient. + /// The place of the recipient. + /// The IBAN of the recipient. + /// The description of the payment. + /// The amount of the payment. + /// The deadline for the payment. + /// The SI model of the recipient. + /// The SI reference of the recipient. + /// The code of the payment. + public SlovenianUpnQr(string payerName, string payerAddress, string payerPlace, string recipientName, string recipientAddress, string recipientPlace, string recipientIban, string description, double amount, DateTime? deadline, string recipientSiModel = "SI99", string recipientSiReference = "", string code = "OTHR") + { + _payerName = LimitLength(payerName.Trim(), 33); + _payerAddress = LimitLength(payerAddress.Trim(), 33); + _payerPlace = LimitLength(payerPlace.Trim(), 33); + _amount = FormatAmount(amount); + _code = LimitLength(code.Trim().ToUpper(), 4); + _purpose = LimitLength(description.Trim(), 42); + _deadLine = (deadline == null) ? "" : deadline.Value.ToString("dd.MM.yyyy"); + _recipientIban = LimitLength(recipientIban.Trim(), 34); + _recipientName = LimitLength(recipientName.Trim(), 33); + _recipientAddress = LimitLength(recipientAddress.Trim(), 33); + _recipientPlace = LimitLength(recipientPlace.Trim(), 33); + _recipientSiModel = LimitLength(recipientSiModel.Trim().ToUpper(), 4); + _recipientSiReference = LimitLength(recipientSiReference.Trim(), 22); + } - /// - /// Formats the amount as a string with leading zeros. - /// - /// The amount to format. - /// The formatted amount string. - private string FormatAmount(double amount) - { - int _amt = (int)Math.Round(amount * 100.0); - return String.Format("{0:00000000000}", _amt); - } + /// + /// Formats the amount as a string with leading zeros. + /// + /// The amount to format. + /// The formatted amount string. + private string FormatAmount(double amount) + { + int _amt = (int)Math.Round(amount * 100.0); + return string.Format("{0:00000000000}", _amt); + } - /// - /// Calculates the checksum of the payment data. - /// - /// The checksum of the payment data. - private int CalculateChecksum() - { - int _cs = 5 + _payerName.Length; //5 = UPNQR constant Length - _cs += _payerAddress.Length; - _cs += _payerPlace.Length; - _cs += _amount.Length; - _cs += _code.Length; - _cs += _purpose.Length; - _cs += _deadLine.Length; - _cs += _recipientIban.Length; - _cs += _recipientName.Length; - _cs += _recipientAddress.Length; - _cs += _recipientPlace.Length; - _cs += _recipientSiModel.Length; - _cs += _recipientSiReference.Length; - _cs += 19; - return _cs; - } + /// + /// Calculates the checksum of the payment data. + /// + /// The checksum of the payment data. + private int CalculateChecksum() + { + int _cs = 5 + _payerName.Length; //5 = UPNQR constant Length + _cs += _payerAddress.Length; + _cs += _payerPlace.Length; + _cs += _amount.Length; + _cs += _code.Length; + _cs += _purpose.Length; + _cs += _deadLine.Length; + _cs += _recipientIban.Length; + _cs += _recipientName.Length; + _cs += _recipientAddress.Length; + _cs += _recipientPlace.Length; + _cs += _recipientSiModel.Length; + _cs += _recipientSiReference.Length; + _cs += 19; + return _cs; + } - /// - /// Returns the Slovenian UPN QR payment data as a string. - /// - /// The Slovenian UPN QR payment data as a string. - public override string ToString() - { - var _sb = new StringBuilder(); - _sb.Append("UPNQR"); - _sb.Append('\n').Append('\n').Append('\n').Append('\n').Append('\n'); - _sb.Append(_payerName).Append('\n'); - _sb.Append(_payerAddress).Append('\n'); - _sb.Append(_payerPlace).Append('\n'); - _sb.Append(_amount).Append('\n').Append('\n').Append('\n'); - _sb.Append(_code.ToUpper()).Append('\n'); - _sb.Append(_purpose).Append('\n'); - _sb.Append(_deadLine).Append('\n'); - _sb.Append(_recipientIban.ToUpper()).Append('\n'); - _sb.Append(_recipientSiModel).Append(_recipientSiReference).Append('\n'); - _sb.Append(_recipientName).Append('\n'); - _sb.Append(_recipientAddress).Append('\n'); - _sb.Append(_recipientPlace).Append('\n'); - _sb.AppendFormat("{0:000}", CalculateChecksum()).Append('\n'); - return _sb.ToString(); - } + /// + /// Returns the Slovenian UPN QR payment data as a string. + /// + /// The Slovenian UPN QR payment data as a string. + public override string ToString() + { + var _sb = new StringBuilder(); + _sb.Append("UPNQR"); + _sb.Append('\n').Append('\n').Append('\n').Append('\n').Append('\n'); + _sb.Append(_payerName).Append('\n'); + _sb.Append(_payerAddress).Append('\n'); + _sb.Append(_payerPlace).Append('\n'); + _sb.Append(_amount).Append('\n').Append('\n').Append('\n'); + _sb.Append(_code.ToUpper()).Append('\n'); + _sb.Append(_purpose).Append('\n'); + _sb.Append(_deadLine).Append('\n'); + _sb.Append(_recipientIban.ToUpper()).Append('\n'); + _sb.Append(_recipientSiModel).Append(_recipientSiReference).Append('\n'); + _sb.Append(_recipientName).Append('\n'); + _sb.Append(_recipientAddress).Append('\n'); + _sb.Append(_recipientPlace).Append('\n'); + _sb.AppendFormat("{0:000}", CalculateChecksum()).Append('\n'); + return _sb.ToString(); } } } diff --git a/QRCoder/PayloadGenerator/SwissQrCode.cs b/QRCoder/PayloadGenerator/SwissQrCode.cs index f2f898de..917115a0 100644 --- a/QRCoder/PayloadGenerator/SwissQrCode.cs +++ b/QRCoder/PayloadGenerator/SwissQrCode.cs @@ -1,679 +1,654 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates the payload for a SwissQrCode. + /// + public class SwissQrCode : Payload { + //Keep in mind, that the ECC level has to be set to "M" when generating a SwissQrCode! + //SwissQrCode specification: + // - (de) https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-de.pdf + // - (en) https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf + //Changes between version 1.0 and 2.0: https://www.paymentstandards.ch/dam/downloads/change-documentation-qrr-de.pdf + + private readonly string _br = "\r\n"; + private readonly string? _alternativeProcedure1, _alternativeProcedure2; + private readonly Iban _iban; + private readonly decimal? _amount; + private readonly Contact _creditor; + private readonly Contact? _ultimateCreditor, _debitor; + private readonly Currency _currency; + private readonly DateTime? _requestedDateOfPayment; + private readonly Reference _reference; + private readonly AdditionalInformation _additionalInformation; + + /// + public override QRCodeGenerator.ECCLevel EccLevel => QRCodeGenerator.ECCLevel.M; + /// + public override QRCodeGenerator.EciMode EciMode => QRCodeGenerator.EciMode.Utf8; + /// - /// Generates the payload for a SwissQrCode. + /// Generates the payload for a SwissQrCode v2.0. (Don't forget to use ECC-Level=M, EncodingMode=UTF-8 and to set the Swiss flag icon to the final QR code.) /// - public class SwissQrCode : Payload + /// IBAN object + /// Currency (either EUR or CHF) + /// Creditor (payee) information + /// Reference information + /// Debitor (payer) information + /// Amount + /// Requested date of debitor's payment + /// Ultimate creditor information (use only in consultation with your bank - for future use only!) + /// Optional command for alternative processing mode - line 1 + /// Optional command for alternative processing mode - line 2 + public SwissQrCode(Iban iban, Currency currency, Contact creditor, Reference reference, AdditionalInformation? additionalInformation = null, Contact? debitor = null, decimal? amount = null, DateTime? requestedDateOfPayment = null, Contact? ultimateCreditor = null, string? alternativeProcedure1 = null, string? alternativeProcedure2 = null) { - //Keep in mind, that the ECC level has to be set to "M" when generating a SwissQrCode! - //SwissQrCode specification: - // - (de) https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-de.pdf - // - (en) https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf - //Changes between version 1.0 and 2.0: https://www.paymentstandards.ch/dam/downloads/change-documentation-qrr-de.pdf - - private readonly string br = "\r\n"; - private readonly string? alternativeProcedure1, alternativeProcedure2; - private readonly Iban iban; - private readonly decimal? amount; - private readonly Contact creditor; - private readonly Contact? ultimateCreditor, debitor; - private readonly Currency currency; - private readonly DateTime? requestedDateOfPayment; - private readonly Reference reference; - private readonly AdditionalInformation additionalInformation; - - /// - public override QRCodeGenerator.ECCLevel EccLevel { get { return QRCodeGenerator.ECCLevel.M; } } - /// - public override QRCodeGenerator.EciMode EciMode { get { return QRCodeGenerator.EciMode.Utf8; } } + _iban = iban; - /// - /// Generates the payload for a SwissQrCode v2.0. (Don't forget to use ECC-Level=M, EncodingMode=UTF-8 and to set the Swiss flag icon to the final QR code.) - /// - /// IBAN object - /// Currency (either EUR or CHF) - /// Creditor (payee) information - /// Reference information - /// Debitor (payer) information - /// Amount - /// Requested date of debitor's payment - /// Ultimate creditor information (use only in consultation with your bank - for future use only!) - /// Optional command for alternative processing mode - line 1 - /// Optional command for alternative processing mode - line 2 - public SwissQrCode(Iban iban, Currency currency, Contact creditor, Reference reference, AdditionalInformation? additionalInformation = null, Contact? debitor = null, decimal? amount = null, DateTime? requestedDateOfPayment = null, Contact? ultimateCreditor = null, string? alternativeProcedure1 = null, string? alternativeProcedure2 = null) - { - this.iban = iban; + _creditor = creditor; + _ultimateCreditor = ultimateCreditor; - this.creditor = creditor; - this.ultimateCreditor = ultimateCreditor; + _additionalInformation = additionalInformation ?? new AdditionalInformation(); - this.additionalInformation = additionalInformation ?? new AdditionalInformation(); + if (amount != null && amount.ToString()!.Length > 12) + throw new SwissQrCodeException("Amount (including decimals) must be shorter than 13 places."); + _amount = amount; - if (amount != null && amount.ToString()!.Length > 12) - throw new SwissQrCodeException("Amount (including decimals) must be shorter than 13 places."); - this.amount = amount; + _currency = currency; + _requestedDateOfPayment = requestedDateOfPayment; + _debitor = debitor; - this.currency = currency; - this.requestedDateOfPayment = requestedDateOfPayment; - this.debitor = debitor; + if (iban.IsQrIban && reference.RefType != Reference.ReferenceType.QRR) + throw new SwissQrCodeException("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); + if (!iban.IsQrIban && reference.RefType == Reference.ReferenceType.QRR) + throw new SwissQrCodeException("If non QR-IBAN is used, you have to choose either \"SCOR\" or \"NON\" as reference type!"); + _reference = reference; - if (iban.IsQrIban && reference.RefType != Reference.ReferenceType.QRR) - throw new SwissQrCodeException("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); - if (!iban.IsQrIban && reference.RefType == Reference.ReferenceType.QRR) - throw new SwissQrCodeException("If non QR-IBAN is used, you have to choose either \"SCOR\" or \"NON\" as reference type!"); - this.reference = reference; + if (alternativeProcedure1 != null && alternativeProcedure1.Length > 100) + throw new SwissQrCodeException("Alternative procedure information block 1 must be shorter than 101 chars."); + _alternativeProcedure1 = alternativeProcedure1; + if (alternativeProcedure2 != null && alternativeProcedure2.Length > 100) + throw new SwissQrCodeException("Alternative procedure information block 2 must be shorter than 101 chars."); + _alternativeProcedure2 = alternativeProcedure2; + } - if (alternativeProcedure1 != null && alternativeProcedure1.Length > 100) - throw new SwissQrCodeException("Alternative procedure information block 1 must be shorter than 101 chars."); - this.alternativeProcedure1 = alternativeProcedure1; - if (alternativeProcedure2 != null && alternativeProcedure2.Length > 100) - throw new SwissQrCodeException("Alternative procedure information block 2 must be shorter than 101 chars."); - this.alternativeProcedure2 = alternativeProcedure2; - } + /// + /// Represents additional information for the SwissQrCode. + /// + public class AdditionalInformation + { + private readonly string? _unstructuredMessage, _billInformation; /// - /// Represents additional information for the SwissQrCode. + /// Creates an additional information object. Both parameters are optional and must be shorter than 141 chars in combination. /// - public class AdditionalInformation + /// Unstructured text message + /// Bill information + public AdditionalInformation(string? unstructuredMessage = null, string? billInformation = null) { - private readonly string trailer; - private readonly string? unstructuredMessage, billInformation; - - /// - /// Creates an additional information object. Both parameters are optional and must be shorter than 141 chars in combination. - /// - /// Unstructured text message - /// Bill information - public AdditionalInformation(string? unstructuredMessage = null, string? billInformation = null) - { - if (((unstructuredMessage != null ? unstructuredMessage.Length : 0) + (billInformation != null ? billInformation.Length : 0)) > 140) - throw new SwissQrCodeAdditionalInformationException("Unstructured message and bill information must be shorter than 141 chars in total/combined."); - this.unstructuredMessage = unstructuredMessage; - this.billInformation = billInformation; - this.trailer = "EPD"; - } + if (((unstructuredMessage != null ? unstructuredMessage.Length : 0) + (billInformation != null ? billInformation.Length : 0)) > 140) + throw new SwissQrCodeAdditionalInformationException("Unstructured message and bill information must be shorter than 141 chars in total/combined."); + _unstructuredMessage = unstructuredMessage; + _billInformation = billInformation; + Trailer = "EPD"; + } + + /// + /// Gets the unstructured message. + /// + public string? UnstructureMessage => !string.IsNullOrEmpty(_unstructuredMessage) ? _unstructuredMessage!.Replace("\n", "") : null; + + /// + /// Gets the bill information. + /// + public string? BillInformation => !string.IsNullOrEmpty(_billInformation) ? _billInformation!.Replace("\n", "") : null; + + /// + /// Gets the trailer. + /// + public string Trailer { get; } - /// - /// Gets the unstructured message. - /// - public string? UnstructureMessage - { - get { return !string.IsNullOrEmpty(unstructuredMessage) ? unstructuredMessage!.Replace("\n", "") : null; } - } + /// + /// Represents exceptions specific to SwissQrCode additional information. + /// + public class SwissQrCodeAdditionalInformationException : Exception + { /// - /// Gets the bill information. + /// Initializes a new instance of the class. /// - public string? BillInformation + public SwissQrCodeAdditionalInformationException() { - get { return !string.IsNullOrEmpty(billInformation) ? billInformation!.Replace("\n", "") : null; } } /// - /// Gets the trailer. + /// Initializes a new instance of the class with a specified error message. /// - public string Trailer + /// The message that describes the error. + public SwissQrCodeAdditionalInformationException(string message) + : base(message) { - get { return trailer; } } - /// - /// Represents exceptions specific to SwissQrCode additional information. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class SwissQrCodeAdditionalInformationException : Exception + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public SwissQrCodeAdditionalInformationException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public SwissQrCodeAdditionalInformationException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public SwissQrCodeAdditionalInformationException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public SwissQrCodeAdditionalInformationException(string message, Exception inner) - : base(message, inner) - { - } } } + } + + /// + /// Represents reference information for the SwissQrCode. + /// + public class Reference + { + private readonly string? _reference; + private readonly ReferenceTextType? _referenceTextType; /// - /// Represents reference information for the SwissQrCode. + /// Creates a reference object which must be passed to the SwissQrCode instance /// - public class Reference + /// Type of the reference (QRR, SCOR or NON) + /// Reference text + /// Type of the reference text (QR-reference or Creditor Reference) + public Reference(ReferenceType referenceType, string? reference = null, ReferenceTextType? referenceTextType = null) { - private readonly ReferenceType referenceType; - private readonly string? reference; - private readonly ReferenceTextType? referenceTextType; - - /// - /// Creates a reference object which must be passed to the SwissQrCode instance - /// - /// Type of the reference (QRR, SCOR or NON) - /// Reference text - /// Type of the reference text (QR-reference or Creditor Reference) - public Reference(ReferenceType referenceType, string? reference = null, ReferenceTextType? referenceTextType = null) - { - this.referenceType = referenceType; - this.referenceTextType = referenceTextType; - - if (referenceType == ReferenceType.NON && reference != null) - throw new SwissQrCodeReferenceException("Reference is only allowed when referenceType not equals \"NON\""); - if (referenceType != ReferenceType.NON && reference != null && referenceTextType == null) - throw new SwissQrCodeReferenceException("You have to set an ReferenceTextType when using the reference text."); - if (referenceTextType == ReferenceTextType.QrReference && reference != null && (reference.Length > 27)) - throw new SwissQrCodeReferenceException("QR-references have to be shorter than 28 chars."); - if (referenceTextType == ReferenceTextType.QrReference && reference != null && !Regex.IsMatch(reference, @"^[0-9]+$")) - throw new SwissQrCodeReferenceException("QR-reference must exist out of digits only."); - if (referenceTextType == ReferenceTextType.QrReference && reference != null && !ChecksumMod10(reference)) - throw new SwissQrCodeReferenceException("QR-references is invalid. Checksum error."); - if (referenceTextType == ReferenceTextType.CreditorReferenceIso11649 && reference != null && (reference.Length > 25)) - throw new SwissQrCodeReferenceException("Creditor references (ISO 11649) have to be shorter than 26 chars."); - - this.reference = reference; - } + RefType = referenceType; + _referenceTextType = referenceTextType; + + if (referenceType == ReferenceType.NON && reference != null) + throw new SwissQrCodeReferenceException("Reference is only allowed when referenceType not equals \"NON\""); + if (referenceType != ReferenceType.NON && reference != null && referenceTextType == null) + throw new SwissQrCodeReferenceException("You have to set an ReferenceTextType when using the reference text."); + if (referenceTextType == ReferenceTextType.QrReference && reference != null && (reference.Length > 27)) + throw new SwissQrCodeReferenceException("QR-references have to be shorter than 28 chars."); + if (referenceTextType == ReferenceTextType.QrReference && reference != null && !Regex.IsMatch(reference, @"^[0-9]+$")) + throw new SwissQrCodeReferenceException("QR-reference must exist out of digits only."); + if (referenceTextType == ReferenceTextType.QrReference && reference != null && !ChecksumMod10(reference)) + throw new SwissQrCodeReferenceException("QR-references is invalid. Checksum error."); + if (referenceTextType == ReferenceTextType.CreditorReferenceIso11649 && reference != null && (reference.Length > 25)) + throw new SwissQrCodeReferenceException("Creditor references (ISO 11649) have to be shorter than 26 chars."); + + _reference = reference; + } - /// - /// Gets the reference type. - /// - public ReferenceType RefType { - get { return referenceType; } - } + /// + /// Gets the reference type. + /// + public ReferenceType RefType { get; } - /// - /// Gets the reference text. - /// - public string? ReferenceText - { - get { return !string.IsNullOrEmpty(reference) ? reference!.Replace("\n", "") : null; } - } + /// + /// Gets the reference text. + /// + public string? ReferenceText => !string.IsNullOrEmpty(_reference) ? _reference!.Replace("\n", "") : null; + /// + /// Reference type. When using a QR-IBAN you have to use either "QRR" or "SCOR". + /// + public enum ReferenceType + { /// - /// Reference type. When using a QR-IBAN you have to use either "QRR" or "SCOR". + /// QR Reference /// - public enum ReferenceType - { - /// - /// QR Reference - /// - QRR, - /// - /// Creditor Reference - /// - SCOR, - /// - /// No Reference - /// - NON - } - + QRR, /// - /// Represents the text type for the reference. + /// Creditor Reference /// - public enum ReferenceTextType - { - /// - /// QR Reference Text - /// - QrReference, - /// - /// Creditor Reference ISO 11649 - /// - CreditorReferenceIso11649 - } - + SCOR, /// - /// Represents exceptions specific to SwissQrCode references. + /// No Reference /// - public class SwissQrCodeReferenceException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public SwissQrCodeReferenceException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public SwissQrCodeReferenceException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public SwissQrCodeReferenceException(string message, Exception inner) - : base(message, inner) - { - } - } + NON } /// - /// Represents an IBAN with type information. + /// Represents the text type for the reference. /// - public class Iban + public enum ReferenceTextType { - private string iban; - private IbanType ibanType; - /// - /// IBAN object with type information + /// QR Reference Text /// - /// IBAN - /// Type of IBAN (normal or QR-IBAN) - public Iban(string iban, IbanType ibanType) - { - if (ibanType == IbanType.Iban && !IsValidIban(iban)) - throw new SwissQrCodeIbanException("The IBAN entered isn't valid."); - if (ibanType == IbanType.QrIban && !IsValidQRIban(iban)) - throw new SwissQrCodeIbanException("The QR-IBAN entered isn't valid."); - if (!iban.StartsWith("CH") && !iban.StartsWith("LI")) - throw new SwissQrCodeIbanException("The IBAN must start with \"CH\" or \"LI\"."); - this.iban = iban; - this.ibanType = ibanType; - } - + QrReference, /// - /// Gets a value indicating whether this is a QR-IBAN. + /// Creditor Reference ISO 11649 /// - public bool IsQrIban - { - get { return ibanType == IbanType.QrIban; } - } + CreditorReferenceIso11649 + } + /// + /// Represents exceptions specific to SwissQrCode references. + /// + public class SwissQrCodeReferenceException : Exception + { /// - /// Converts the IBAN object to its string representation. + /// Initializes a new instance of the class. /// - /// A string representation of the IBAN. - public override string ToString() + public SwissQrCodeReferenceException() { - return iban.Replace("-", "").Replace("\n", "").Replace(" ",""); } /// - /// Represents the type of IBAN. + /// Initializes a new instance of the class with a specified error message. /// - public enum IbanType + /// The message that describes the error. + public SwissQrCodeReferenceException(string message) + : base(message) { - /// - /// Regular IBAN - /// - Iban, - /// - /// QR-IBAN - /// - QrIban } /// - /// Represents exceptions specific to SwissQrCode IBANs. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class SwissQrCodeIbanException : Exception + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public SwissQrCodeReferenceException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public SwissQrCodeIbanException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public SwissQrCodeIbanException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception. - public SwissQrCodeIbanException(string message, Exception inner) - : base(message, inner) - { - } } } + } + + /// + /// Represents an IBAN with type information. + /// + public class Iban + { + private readonly string _iban; + private readonly IbanType _ibanType; /// - /// Represents contact information. + /// IBAN object with type information /// - public class Contact + /// IBAN + /// Type of IBAN (normal or QR-IBAN) + public Iban(string iban, IbanType ibanType) { - private static readonly HashSet twoLetterCodes = ValidTwoLetterCodes(); - private string br = "\r\n"; - private string name, zipCode, city, country; - private string? streetOrAddressline1, houseNumberOrAddressline2; - private AddressType adrType; - - /// - /// Contact type. Can be used for payee, ultimate payee, etc. with address in structured mode (S). - /// - /// Last name or company (optional first name) - /// Zip-/Postcode - /// City name - /// Two-letter country code as defined in ISO 3166-1 - /// Streetname without house number - /// House number - [Obsolete("This constructor is deprecated. Use WithStructuredAddress instead.")] - public Contact(string name, string zipCode, string city, string country, string? street = null, string? houseNumber = null) : this (name, zipCode, city, country, street, houseNumber, AddressType.StructuredAddress) - { - } + if (ibanType == IbanType.Iban && !IsValidIban(iban)) + throw new SwissQrCodeIbanException("The IBAN entered isn't valid."); + if (ibanType == IbanType.QrIban && !IsValidQRIban(iban)) + throw new SwissQrCodeIbanException("The QR-IBAN entered isn't valid."); + if (!iban.StartsWith("CH") && !iban.StartsWith("LI")) + throw new SwissQrCodeIbanException("The IBAN must start with \"CH\" or \"LI\"."); + _iban = iban; + _ibanType = ibanType; + } + /// + /// Gets a value indicating whether this is a QR-IBAN. + /// + public bool IsQrIban => _ibanType == IbanType.QrIban; - /// - /// Contact type. Can be used for payee, ultimate payee, etc. with address in combined mode (K). - /// - /// Last name or company (optional first name) - /// Two-letter country code as defined in ISO 3166-1 - /// Adress line 1 - /// Adress line 2 - [Obsolete("This constructor is deprecated. Use WithCombinedAddress instead.")] - public Contact(string name, string country, string addressLine1, string addressLine2) : this(name, null, null, country, addressLine1, addressLine2, AddressType.CombinedAddress) - { - } + /// + /// Converts the IBAN object to its string representation. + /// + /// A string representation of the IBAN. + public override string ToString() + => _iban.Replace("-", "").Replace("\n", "").Replace(" ", ""); + /// + /// Represents the type of IBAN. + /// + public enum IbanType + { /// - /// Creates a contact with structured address. + /// Regular IBAN /// - public static Contact WithStructuredAddress(string name, string zipCode, string city, string country, string? street = null, string? houseNumber = null) - { - return new Contact(name, zipCode, city, country, street, houseNumber, AddressType.StructuredAddress); - } - + Iban, /// - /// Creates a contact with combined address. + /// QR-IBAN /// - public static Contact WithCombinedAddress(string name, string country, string addressLine1, string addressLine2) - { - return new Contact(name, null, null, country, addressLine1, addressLine2, AddressType.CombinedAddress); - } - - - private Contact(string name, string? zipCode, string? city, string country, string? streetOrAddressline1, string? houseNumberOrAddressline2, AddressType addressType) - { - //Pattern extracted from https://qr-validation.iso-payments.ch as explained in https://github.com/codebude/QRCoder/issues/97 - var charsetPattern = @"^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"; - - this.adrType = addressType; - - if (string.IsNullOrEmpty(name)) - throw new SwissQrCodeContactException("Name must not be empty."); - if (name.Length > 70) - throw new SwissQrCodeContactException("Name must be shorter than 71 chars."); - if (!Regex.IsMatch(name, charsetPattern)) - throw new SwissQrCodeContactException($"Name must match the following pattern as defined in pain.001: {charsetPattern}"); - this.name = name; - - if (AddressType.StructuredAddress == this.adrType) - { - if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1!.Length > 70)) - throw new SwissQrCodeContactException("Street must be shorter than 71 chars."); - if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, charsetPattern)) - throw new SwissQrCodeContactException($"Street must match the following pattern as defined in pain.001: {charsetPattern}"); - this.streetOrAddressline1 = streetOrAddressline1; - - if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && houseNumberOrAddressline2!.Length > 16) - throw new SwissQrCodeContactException("House number must be shorter than 17 chars."); - this.houseNumberOrAddressline2 = houseNumberOrAddressline2; - } - else - { - if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1!.Length > 70)) - throw new SwissQrCodeContactException("Address line 1 must be shorter than 71 chars."); - if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, charsetPattern)) - throw new SwissQrCodeContactException($"Address line 1 must match the following pattern as defined in pain.001: {charsetPattern}"); - this.streetOrAddressline1 = streetOrAddressline1; - - if (string.IsNullOrEmpty(houseNumberOrAddressline2)) - throw new SwissQrCodeContactException("Address line 2 must be provided for combined addresses (address line-based addresses)."); - if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && (houseNumberOrAddressline2!.Length > 70)) - throw new SwissQrCodeContactException("Address line 2 must be shorter than 71 chars."); - if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && !Regex.IsMatch(houseNumberOrAddressline2, charsetPattern)) - throw new SwissQrCodeContactException($"Address line 2 must match the following pattern as defined in pain.001: {charsetPattern}"); - this.houseNumberOrAddressline2 = houseNumberOrAddressline2; - } - - if (AddressType.StructuredAddress == this.adrType) { - if (string.IsNullOrEmpty(zipCode)) - throw new SwissQrCodeContactException("Zip code must not be empty."); - if (zipCode!.Length > 16) - throw new SwissQrCodeContactException("Zip code must be shorter than 17 chars."); - if (!Regex.IsMatch(zipCode, charsetPattern)) - throw new SwissQrCodeContactException($"Zip code must match the following pattern as defined in pain.001: {charsetPattern}"); - this.zipCode = zipCode; - - if (string.IsNullOrEmpty(city)) - throw new SwissQrCodeContactException("City must not be empty."); - if (city!.Length > 35) - throw new SwissQrCodeContactException("City name must be shorter than 36 chars."); - if (!Regex.IsMatch(city, charsetPattern)) - throw new SwissQrCodeContactException($"City name must match the following pattern as defined in pain.001: {charsetPattern}"); - this.city = city; - } - else - { - this.zipCode = this.city = string.Empty; - } - - if (!IsValidTwoLetterCode(country)) - throw new SwissQrCodeContactException("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); - - this.country = country; - } - - private static bool IsValidTwoLetterCode(string code) => twoLetterCodes.Contains(code); - - private static HashSet ValidTwoLetterCodes() - { - string[] codes = new string[]{ "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "MK", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX", "XK" }; - return new HashSet(codes, StringComparer.OrdinalIgnoreCase); - } + QrIban + } + /// + /// Represents exceptions specific to SwissQrCode IBANs. + /// + public class SwissQrCodeIbanException : Exception + { /// - /// Returns a string that represents the contact information in the format required for Swiss QR codes. + /// Initializes a new instance of the class. /// - /// A string representing the contact information. - public override string ToString() + public SwissQrCodeIbanException() { - string contactData = $"{(AddressType.StructuredAddress == adrType ? "S" : "K")}{br}"; //AdrTp - contactData += name.Replace("\n", "") + br; //Name - contactData += (!string.IsNullOrEmpty(streetOrAddressline1) ? streetOrAddressline1!.Replace("\n","") : string.Empty) + br; //StrtNmOrAdrLine1 - contactData += (!string.IsNullOrEmpty(houseNumberOrAddressline2) ? houseNumberOrAddressline2!.Replace("\n", "") : string.Empty) + br; //BldgNbOrAdrLine2 - contactData += zipCode.Replace("\n", "") + br; //PstCd - contactData += city.Replace("\n", "") + br; //TwnNm - contactData += country + br; //Ctry - return contactData; } /// - /// Defines the type of address. + /// Initializes a new instance of the class with a specified error message. /// - public enum AddressType + /// The message that describes the error. + public SwissQrCodeIbanException(string message) + : base(message) { - /// - /// Structured Address - /// - StructuredAddress, - /// - /// Combined Address - /// - CombinedAddress } /// - /// Represents errors that occur during the creation of a Swiss QR code contact. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// - public class SwissQrCodeContactException : Exception + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception. + public SwissQrCodeIbanException(string message, Exception inner) + : base(message, inner) { - /// - /// Initializes a new instance of the class. - /// - public SwissQrCodeContactException() - { - } - - /// - /// Initializes a new instance of the class with a specified error message. - /// - /// The message that describes the error. - public SwissQrCodeContactException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public SwissQrCodeContactException(string message, Exception inner) - : base(message, inner) - { - } } } + } + + /// + /// Represents contact information. + /// + public class Contact + { + private static readonly HashSet _twoLetterCodes = ValidTwoLetterCodes(); + private readonly string _br = "\r\n"; + private readonly string _name, _zipCode, _city, _country; + private readonly string? _streetOrAddressline1, _houseNumberOrAddressline2; + private readonly AddressType _adrType; /// - /// Returns a string that represents the entire Swiss QR code payload. + /// Contact type. Can be used for payee, ultimate payee, etc. with address in structured mode (S). /// - /// A string representing the Swiss QR code payload. - public override string ToString() + /// Last name or company (optional first name) + /// Zip-/Postcode + /// City name + /// Two-letter country code as defined in ISO 3166-1 + /// Streetname without house number + /// House number + [Obsolete("This constructor is deprecated. Use WithStructuredAddress instead.")] + public Contact(string name, string zipCode, string city, string country, string? street = null, string? houseNumber = null) : this(name, zipCode, city, country, street, houseNumber, AddressType.StructuredAddress) { - //Header "logical" element - var SwissQrCodePayload = "SPC" + br; //QRType - SwissQrCodePayload += "0200" + br; //Version - SwissQrCodePayload += "1" + br; //Coding + } - //CdtrInf "logical" element - SwissQrCodePayload += iban.ToString() + br; //IBAN + /// + /// Contact type. Can be used for payee, ultimate payee, etc. with address in combined mode (K). + /// + /// Last name or company (optional first name) + /// Two-letter country code as defined in ISO 3166-1 + /// Adress line 1 + /// Adress line 2 + [Obsolete("This constructor is deprecated. Use WithCombinedAddress instead.")] + public Contact(string name, string country, string addressLine1, string addressLine2) : this(name, null, null, country, addressLine1, addressLine2, AddressType.CombinedAddress) + { + } - //Cdtr "logical" element - SwissQrCodePayload += creditor.ToString(); + /// + /// Creates a contact with structured address. + /// + public static Contact WithStructuredAddress(string name, string zipCode, string city, string country, string? street = null, string? houseNumber = null) + => new Contact(name, zipCode, city, country, street, houseNumber, AddressType.StructuredAddress); - //UltmtCdtr "logical" element - //Since version 2.0 ultimate creditor was marked as "for future use" and has to be delivered empty in any case! - SwissQrCodePayload += string.Concat(Enumerable.Repeat(br, 7).ToArray()); + /// + /// Creates a contact with combined address. + /// + public static Contact WithCombinedAddress(string name, string country, string addressLine1, string addressLine2) + => new Contact(name, null, null, country, addressLine1, addressLine2, AddressType.CombinedAddress); - //CcyAmtDate "logical" element - //Amoutn has to use . as decimal seperator in any case. See https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf page 27. - SwissQrCodePayload += (amount != null ? $"{amount:0.00}".Replace(",", ".") : string.Empty) + br; //Amt - SwissQrCodePayload += currency + br; //Ccy - //Removed in S-QR version 2.0 - //SwissQrCodePayload += (requestedDateOfPayment != null ? ((DateTime)requestedDateOfPayment).ToString("yyyy-MM-dd") : string.Empty) + br; //ReqdExctnDt - //UltmtDbtr "logical" element - if (debitor != null) - SwissQrCodePayload += debitor.ToString(); - else - SwissQrCodePayload += string.Concat(Enumerable.Repeat(br, 7).ToArray()); + private Contact(string name, string? zipCode, string? city, string country, string? streetOrAddressline1, string? houseNumberOrAddressline2, AddressType addressType) + { + //Pattern extracted from https://qr-validation.iso-payments.ch as explained in https://github.com/codebude/QRCoder/issues/97 + var charsetPattern = @"^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"; + _adrType = addressType; - //RmtInf "logical" element - SwissQrCodePayload += reference.RefType.ToString() + br; //Tp - SwissQrCodePayload += (!string.IsNullOrEmpty(reference.ReferenceText) ? reference.ReferenceText : string.Empty) + br; //Ref - + if (string.IsNullOrEmpty(name)) + throw new SwissQrCodeContactException("Name must not be empty."); + if (name.Length > 70) + throw new SwissQrCodeContactException("Name must be shorter than 71 chars."); + if (!Regex.IsMatch(name, charsetPattern)) + throw new SwissQrCodeContactException($"Name must match the following pattern as defined in pain.001: {charsetPattern}"); + _name = name; - //AddInf "logical" element - SwissQrCodePayload += (!string.IsNullOrEmpty(additionalInformation.UnstructureMessage) ? additionalInformation.UnstructureMessage : string.Empty) + br; //Ustrd - SwissQrCodePayload += additionalInformation.Trailer + br; //Trailer - // Bugfix PR #399 If BillInformation is empty, insert no linebreak - SwissQrCodePayload += (!string.IsNullOrEmpty(additionalInformation.BillInformation) ? additionalInformation.BillInformation + br : string.Empty); //StrdBkgInf + if (AddressType.StructuredAddress == _adrType) + { + if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1!.Length > 70)) + throw new SwissQrCodeContactException("Street must be shorter than 71 chars."); + if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, charsetPattern)) + throw new SwissQrCodeContactException($"Street must match the following pattern as defined in pain.001: {charsetPattern}"); + _streetOrAddressline1 = streetOrAddressline1; + + if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && houseNumberOrAddressline2!.Length > 16) + throw new SwissQrCodeContactException("House number must be shorter than 17 chars."); + _houseNumberOrAddressline2 = houseNumberOrAddressline2; + } + else + { + if (!string.IsNullOrEmpty(streetOrAddressline1) && (streetOrAddressline1!.Length > 70)) + throw new SwissQrCodeContactException("Address line 1 must be shorter than 71 chars."); + if (!string.IsNullOrEmpty(streetOrAddressline1) && !Regex.IsMatch(streetOrAddressline1, charsetPattern)) + throw new SwissQrCodeContactException($"Address line 1 must match the following pattern as defined in pain.001: {charsetPattern}"); + _streetOrAddressline1 = streetOrAddressline1; + + if (string.IsNullOrEmpty(houseNumberOrAddressline2)) + throw new SwissQrCodeContactException("Address line 2 must be provided for combined addresses (address line-based addresses)."); + if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && (houseNumberOrAddressline2!.Length > 70)) + throw new SwissQrCodeContactException("Address line 2 must be shorter than 71 chars."); + if (!string.IsNullOrEmpty(houseNumberOrAddressline2) && !Regex.IsMatch(houseNumberOrAddressline2, charsetPattern)) + throw new SwissQrCodeContactException($"Address line 2 must match the following pattern as defined in pain.001: {charsetPattern}"); + _houseNumberOrAddressline2 = houseNumberOrAddressline2; + } - //AltPmtInf "logical" element - if (!string.IsNullOrEmpty(alternativeProcedure1)) - SwissQrCodePayload += alternativeProcedure1!.Replace("\n", "") + br; //AltPmt - if (!string.IsNullOrEmpty(alternativeProcedure2)) - SwissQrCodePayload += alternativeProcedure2!.Replace("\n", "") + br; //AltPmt + if (AddressType.StructuredAddress == _adrType) + { + if (string.IsNullOrEmpty(zipCode)) + throw new SwissQrCodeContactException("Zip code must not be empty."); + if (zipCode!.Length > 16) + throw new SwissQrCodeContactException("Zip code must be shorter than 17 chars."); + if (!Regex.IsMatch(zipCode, charsetPattern)) + throw new SwissQrCodeContactException($"Zip code must match the following pattern as defined in pain.001: {charsetPattern}"); + _zipCode = zipCode; + + if (string.IsNullOrEmpty(city)) + throw new SwissQrCodeContactException("City must not be empty."); + if (city!.Length > 35) + throw new SwissQrCodeContactException("City name must be shorter than 36 chars."); + if (!Regex.IsMatch(city, charsetPattern)) + throw new SwissQrCodeContactException($"City name must match the following pattern as defined in pain.001: {charsetPattern}"); + _city = city; + } + else + { + _zipCode = _city = string.Empty; + } - //S-QR specification 2.0, chapter 4.2.3 - if (SwissQrCodePayload.EndsWith(br)) - SwissQrCodePayload = SwissQrCodePayload.Remove(SwissQrCodePayload.Length - br.Length); + if (!IsValidTwoLetterCode(country)) + throw new SwissQrCodeContactException("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); - return SwissQrCodePayload; + _country = country; } + private static bool IsValidTwoLetterCode(string code) => _twoLetterCodes.Contains(code); + private static HashSet ValidTwoLetterCodes() + { + string[] codes = new string[] { "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "MK", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", "AX", "XK" }; + return new HashSet(codes, StringComparer.OrdinalIgnoreCase); + } + /// + /// Returns a string that represents the contact information in the format required for Swiss QR codes. + /// + /// A string representing the contact information. + public override string ToString() + { + string contactData = $"{(AddressType.StructuredAddress == _adrType ? "S" : "K")}{_br}"; //AdrTp + contactData += _name.Replace("\n", "") + _br; //Name + contactData += (!string.IsNullOrEmpty(_streetOrAddressline1) ? _streetOrAddressline1!.Replace("\n", "") : string.Empty) + _br; //StrtNmOrAdrLine1 + contactData += (!string.IsNullOrEmpty(_houseNumberOrAddressline2) ? _houseNumberOrAddressline2!.Replace("\n", "") : string.Empty) + _br; //BldgNbOrAdrLine2 + contactData += _zipCode.Replace("\n", "") + _br; //PstCd + contactData += _city.Replace("\n", "") + _br; //TwnNm + contactData += _country + _br; //Ctry + return contactData; + } /// - /// ISO 4217 currency codes. + /// Defines the type of address. /// - public enum Currency + public enum AddressType { /// - /// Swiss Franc + /// Structured Address /// - CHF = 756, + StructuredAddress, /// - /// Euro + /// Combined Address /// - EUR = 978 + CombinedAddress } /// - /// Represents errors that occur during Swiss QR Code generation. + /// Represents errors that occur during the creation of a Swiss QR code contact. /// - public class SwissQrCodeException : Exception + public class SwissQrCodeContactException : Exception { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public SwissQrCodeException() + public SwissQrCodeContactException() { } /// - /// Initializes a new instance of the class with a specified error message. + /// Initializes a new instance of the class with a specified error message. /// /// The message that describes the error. - public SwissQrCodeException(string message) + public SwissQrCodeContactException(string message) : base(message) { } /// - /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. - public SwissQrCodeException(string message, Exception inner) + public SwissQrCodeContactException(string message, Exception inner) : base(message, inner) { } } } + + /// + /// Returns a string that represents the entire Swiss QR code payload. + /// + /// A string representing the Swiss QR code payload. + public override string ToString() + { + //Header "logical" element + var SwissQrCodePayload = "SPC" + _br; //QRType + SwissQrCodePayload += "0200" + _br; //Version + SwissQrCodePayload += "1" + _br; //Coding + + //CdtrInf "logical" element + SwissQrCodePayload += _iban.ToString() + _br; //IBAN + + + //Cdtr "logical" element + SwissQrCodePayload += _creditor.ToString(); + + //UltmtCdtr "logical" element + //Since version 2.0 ultimate creditor was marked as "for future use" and has to be delivered empty in any case! + SwissQrCodePayload += string.Concat(Enumerable.Repeat(_br, 7).ToArray()); + + //CcyAmtDate "logical" element + //Amoutn has to use . as decimal seperator in any case. See https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf page 27. + SwissQrCodePayload += (_amount != null ? $"{_amount:0.00}".Replace(",", ".") : string.Empty) + _br; //Amt + SwissQrCodePayload += _currency + _br; //Ccy + //Removed in S-QR version 2.0 + //SwissQrCodePayload += (requestedDateOfPayment != null ? ((DateTime)requestedDateOfPayment).ToString("yyyy-MM-dd") : string.Empty) + br; //ReqdExctnDt + + //UltmtDbtr "logical" element + if (_debitor != null) + SwissQrCodePayload += _debitor.ToString(); + else + SwissQrCodePayload += string.Concat(Enumerable.Repeat(_br, 7).ToArray()); + + + //RmtInf "logical" element + SwissQrCodePayload += _reference.RefType.ToString() + _br; //Tp + SwissQrCodePayload += (!string.IsNullOrEmpty(_reference.ReferenceText) ? _reference.ReferenceText : string.Empty) + _br; //Ref + + + //AddInf "logical" element + SwissQrCodePayload += (!string.IsNullOrEmpty(_additionalInformation.UnstructureMessage) ? _additionalInformation.UnstructureMessage : string.Empty) + _br; //Ustrd + SwissQrCodePayload += _additionalInformation.Trailer + _br; //Trailer + // Bugfix PR #399 If BillInformation is empty, insert no linebreak + SwissQrCodePayload += (!string.IsNullOrEmpty(_additionalInformation.BillInformation) ? _additionalInformation.BillInformation + _br : string.Empty); //StrdBkgInf + + //AltPmtInf "logical" element + if (!string.IsNullOrEmpty(_alternativeProcedure1)) + SwissQrCodePayload += _alternativeProcedure1!.Replace("\n", "") + _br; //AltPmt + if (!string.IsNullOrEmpty(_alternativeProcedure2)) + SwissQrCodePayload += _alternativeProcedure2!.Replace("\n", "") + _br; //AltPmt + + //S-QR specification 2.0, chapter 4.2.3 + if (SwissQrCodePayload.EndsWith(_br)) + SwissQrCodePayload = SwissQrCodePayload.Remove(SwissQrCodePayload.Length - _br.Length); + + return SwissQrCodePayload; + } + + + + + /// + /// ISO 4217 currency codes. + /// + public enum Currency + { + /// + /// Swiss Franc + /// + CHF = 756, + /// + /// Euro + /// + EUR = 978 + } + + /// + /// Represents errors that occur during Swiss QR Code generation. + /// + public class SwissQrCodeException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public SwissQrCodeException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public SwissQrCodeException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + public SwissQrCodeException(string message, Exception inner) + : base(message, inner) + { + } + } } } diff --git a/QRCoder/PayloadGenerator/Url.cs b/QRCoder/PayloadGenerator/Url.cs index e3580c8c..80a25d11 100644 --- a/QRCoder/PayloadGenerator/Url.cs +++ b/QRCoder/PayloadGenerator/Url.cs @@ -1,33 +1,30 @@ -using System; +using System; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a URL payload for QR codes. + /// + public class Url : Payload { + private readonly string _url; + /// - /// Generates a URL payload for QR codes. + /// Initializes a new instance of the class. /// - public class Url : Payload + /// The target URL. If the protocol is not specified, the http protocol will be added. + public Url(string url) { - private readonly string url; - - /// - /// Initializes a new instance of the class. - /// - /// The target URL. If the protocol is not specified, the http protocol will be added. - public Url(string url) - { - this.url = url; - } - - /// - /// Returns the URL payload as a string. - /// - /// The URL payload as a string, ensuring it starts with "http://" if no protocol is specified. - public override string ToString() - { - return (!this.url.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? "http://" + this.url : this.url); - } + _url = url; } + + /// + /// Returns the URL payload as a string. + /// + /// The URL payload as a string, ensuring it starts with "http://" if no protocol is specified. + public override string ToString() + => !_url.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? "http://" + _url : _url; } } diff --git a/QRCoder/PayloadGenerator/WhatsAppMessage.cs b/QRCoder/PayloadGenerator/WhatsAppMessage.cs index 9091b00a..30126275 100644 --- a/QRCoder/PayloadGenerator/WhatsAppMessage.cs +++ b/QRCoder/PayloadGenerator/WhatsAppMessage.cs @@ -1,51 +1,50 @@ -using System; +using System; using System.Text.RegularExpressions; -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a WhatsApp message payload for QR codes. + /// + public class WhatsAppMessage : Payload { + private readonly string _number, _message; + /// - /// Generates a WhatsApp message payload for QR codes. + /// Initializes a new instance of the class with a receiver number and message. /// - public class WhatsAppMessage : Payload + /// + /// Receiver phone number in full international format. + /// Omit any zeroes, brackets, or dashes. Use format: 1XXXXXXXXXX. Don't use: +001-(XXX)XXXXXXX. + /// + /// The message to be sent. + public WhatsAppMessage(string number, string message) { - private readonly string number, message; - - /// - /// Initializes a new instance of the class with a receiver number and message. - /// - /// - /// Receiver phone number in full international format. - /// Omit any zeroes, brackets, or dashes. Use format: 1XXXXXXXXXX. Don't use: +001-(XXX)XXXXXXX. - /// - /// The message to be sent. - public WhatsAppMessage(string number, string message) - { - this.number = number; - this.message = message; - } + _number = number; + _message = message; + } - /// - /// Initializes a new instance of the class with a message only. - /// When scanned, the user is asked to choose a contact to receive the message. - /// - /// The message to be sent. - public WhatsAppMessage(string message) - { - this.number = string.Empty; - this.message = message; - } + /// + /// Initializes a new instance of the class with a message only. + /// When scanned, the user is asked to choose a contact to receive the message. + /// + /// The message to be sent. + public WhatsAppMessage(string message) + { + _number = string.Empty; + _message = message; + } - /// - /// Returns the WhatsApp message payload as a string. - /// - /// The WhatsApp message URL as a string. - public override string ToString() - { - var cleanedPhone = Regex.Replace(this.number, @"^[0+]+|[ ()-]", string.Empty); - return ($"https://wa.me/{cleanedPhone}?text={Uri.EscapeDataString(message)}"); - } + /// + /// Returns the WhatsApp message payload as a string. + /// + /// The WhatsApp message URL as a string. + public override string ToString() + { + var cleanedPhone = Regex.Replace(_number, @"^[0+]+|[ ()-]", string.Empty); + return ($"https://wa.me/{cleanedPhone}?text={Uri.EscapeDataString(_message)}"); } } } diff --git a/QRCoder/PayloadGenerator/WiFi.cs b/QRCoder/PayloadGenerator/WiFi.cs index 9e699977..f9c45781 100644 --- a/QRCoder/PayloadGenerator/WiFi.cs +++ b/QRCoder/PayloadGenerator/WiFi.cs @@ -1,64 +1,60 @@ -namespace QRCoder +namespace QRCoder; + +public static partial class PayloadGenerator { - public static partial class PayloadGenerator + /// + /// Generates a WiFi payload. Scanned by a QR Code scanner app, the device will connect to the WiFi. + /// + public class WiFi : Payload { + private readonly string _ssid, _password, _authenticationMode; + private readonly bool _isHiddenSsid; + /// - /// Generates a WiFi payload. Scanned by a QR Code scanner app, the device will connect to the WiFi. + /// Initializes a new instance of the class. /// - public class WiFi : Payload + /// SSID of the WiFi network + /// Password of the WiFi network + /// Authentication mode (WEP, WPA, WPA2) + /// Set flag if the WiFi network hides its SSID + /// Set flag if ssid/password is delivered as HEX string. Note: May not be supported on iOS devices. + public WiFi(string ssid, string password, Authentication authenticationMode, bool isHiddenSSID = false, bool escapeHexStrings = true) { - private readonly string ssid, password, authenticationMode; - private readonly bool isHiddenSsid; + _ssid = EscapeInput(ssid); + _ssid = escapeHexStrings && isHexStyle(_ssid) ? "\"" + _ssid + "\"" : _ssid; + _password = EscapeInput(password); + _password = escapeHexStrings && isHexStyle(_password) ? "\"" + _password + "\"" : _password; + _authenticationMode = authenticationMode.ToString(); + _isHiddenSsid = isHiddenSSID; + } + /// + /// Returns the WiFi payload as a string. + /// + public override string ToString() + => $"WIFI:T:{_authenticationMode};S:{_ssid};P:{_password};{(_isHiddenSsid ? "H:true" : string.Empty)};"; + + /// + /// Specifies the authentication mode for the WiFi network. + /// + public enum Authentication + { /// - /// Initializes a new instance of the class. + /// WEP authentication mode /// - /// SSID of the WiFi network - /// Password of the WiFi network - /// Authentication mode (WEP, WPA, WPA2) - /// Set flag if the WiFi network hides its SSID - /// Set flag if ssid/password is delivered as HEX string. Note: May not be supported on iOS devices. - public WiFi(string ssid, string password, Authentication authenticationMode, bool isHiddenSSID = false, bool escapeHexStrings = true) - { - this.ssid = EscapeInput(ssid); - this.ssid = escapeHexStrings && isHexStyle(this.ssid) ? "\"" + this.ssid + "\"" : this.ssid; - this.password = EscapeInput(password); - this.password = escapeHexStrings && isHexStyle(this.password) ? "\"" + this.password + "\"" : this.password; - this.authenticationMode = authenticationMode.ToString(); - this.isHiddenSsid = isHiddenSSID; - } - + WEP, /// - /// Returns the WiFi payload as a string. + /// WPA authentication mode /// - public override string ToString() - { - return - $"WIFI:T:{this.authenticationMode};S:{this.ssid};P:{this.password};{(this.isHiddenSsid ? "H:true" : string.Empty)};"; - } - + WPA, + /// + /// No password authentication mode + /// + nopass, /// - /// Specifies the authentication mode for the WiFi network. + /// WPA2 authentication mode /// - public enum Authentication - { - /// - /// WEP authentication mode - /// - WEP, - /// - /// WPA authentication mode - /// - WPA, - /// - /// No password authentication mode - /// - nopass, - /// - /// WPA2 authentication mode - /// - WPA2 - } + WPA2 } } } diff --git a/QRCoder/PdfByteQRCode.cs b/QRCoder/PdfByteQRCode.cs index 8b039901..1492bec5 100644 --- a/QRCoder/PdfByteQRCode.cs +++ b/QRCoder/PdfByteQRCode.cs @@ -1,4 +1,4 @@ -#if SYSTEM_DRAWING +#if SYSTEM_DRAWING using System; using System.Collections.Generic; using System.Drawing.Imaging; @@ -8,267 +8,260 @@ using static QRCoder.QRCodeGenerator; /* This renderer is inspired by RemusVasii: https://github.com/codebude/QRCoder/issues/223 */ -namespace QRCoder +namespace QRCoder; + + +/// +/// Represents a QR code generator that outputs QR codes as PDF byte arrays. +/// +#if NET6_0_OR_GREATER +[System.Runtime.Versioning.SupportedOSPlatform("windows")] +#endif +// ReSharper disable once InconsistentNaming +public class PdfByteQRCode : AbstractQRCode, IDisposable { + private readonly byte[] _pdfBinaryComment = new byte[] { 0x25, 0xe2, 0xe3, 0xcf, 0xd3 }; /// - /// Represents a QR code generator that outputs QR codes as PDF byte arrays. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. /// -#if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] -#endif - // ReSharper disable once InconsistentNaming - public class PdfByteQRCode : AbstractQRCode, IDisposable + public PdfByteQRCode() { } + + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public PdfByteQRCode(QRCodeData data) : base(data) { } + + /// + /// Creates a PDF document with a black and white QR code. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a PDF byte array. + public byte[] GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, "#000000", "#ffffff"); + + /// + /// Converts a hexadecimal color string to a byte array. + /// + /// Color in HEX format like #ffffff. + /// Returns the color as a byte array. + private byte[] HexColorToByteArray(string colorString) { - private readonly byte[] pdfBinaryComment = new byte[] { 0x25, 0xe2, 0xe3, 0xcf, 0xd3 }; - - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public PdfByteQRCode() { } - - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public PdfByteQRCode(QRCodeData data) : base(data) { } - - /// - /// Creates a PDF document with a black and white QR code. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a PDF byte array. - public byte[] GetGraphic(int pixelsPerModule) - { - return GetGraphic(pixelsPerModule, "#000000", "#ffffff"); - } + if (colorString.StartsWith("#")) + colorString = colorString.Substring(1); + byte[] byteColor = new byte[colorString.Length / 2]; + for (int i = 0; i < byteColor.Length; i++) + byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); + return byteColor; + } - /// - /// Converts a hexadecimal color string to a byte array. - /// - /// Color in HEX format like #ffffff. - /// Returns the color as a byte array. - private byte[] HexColorToByteArray(string colorString) + /// + /// Creates a PDF document with specified colors, DPI, and quality. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// The DPI (dots per inch) of the PDF document. + /// The JPEG quality of the PDF document. + /// Returns the QR code graphic as a PDF byte array. + public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, int dpi = 150, long jpgQuality = 85) + { + byte[] jpgArray, pngArray; + var imgSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule; + var pdfMediaSize = (imgSize * 72 / dpi).ToString(CultureInfo.InvariantCulture); + + //Get QR code image + using (var qrCode = new PngByteQRCode(QrCodeData)) { - if (colorString.StartsWith("#")) - colorString = colorString.Substring(1); - byte[] byteColor = new byte[colorString.Length / 2]; - for (int i = 0; i < byteColor.Length; i++) - byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture); - return byteColor; + pngArray = qrCode.GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex)); } - /// - /// Creates a PDF document with specified colors, DPI, and quality. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// The DPI (dots per inch) of the PDF document. - /// The JPEG quality of the PDF document. - /// Returns the QR code graphic as a PDF byte array. - public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, int dpi = 150, long jpgQuality = 85) + //Create image and transofrm to JPG + using (var msPng = new MemoryStream()) { - byte[] jpgArray, pngArray; - var imgSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule; - var pdfMediaSize = (imgSize * 72 / dpi).ToString(CultureInfo.InvariantCulture); - - //Get QR code image - using (var qrCode = new PngByteQRCode(QrCodeData)) + msPng.Write(pngArray, 0, pngArray.Length); + var img = System.Drawing.Image.FromStream(msPng); + using var msJpeg = new MemoryStream(); + // Create JPEG with specified quality + var jpgImageCodecInfo = ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == "image/jpeg"); + var jpgEncoderParameters = new EncoderParameters(1) { - pngArray = qrCode.GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex)); - } - - //Create image and transofrm to JPG - using (var msPng = new MemoryStream()) - { - msPng.Write(pngArray, 0, pngArray.Length); - var img = System.Drawing.Image.FromStream(msPng); - using (var msJpeg = new MemoryStream()) - { - // Create JPEG with specified quality - var jpgImageCodecInfo = ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == "image/jpeg"); - var jpgEncoderParameters = new EncoderParameters(1) { - Param = new EncoderParameter[]{ new EncoderParameter(Encoder.Quality, jpgQuality) } - }; - img.Save(msJpeg, jpgImageCodecInfo, jpgEncoderParameters); - jpgArray = msJpeg.ToArray(); - } - } - - //Create PDF document - using (var stream = new MemoryStream()) - { - var writer = new StreamWriter(stream, System.Text.Encoding.GetEncoding("ASCII")); - - var xrefs = new List(); - - writer.Write("%PDF-1.5\r\n"); - writer.Flush(); - - stream.Write(pdfBinaryComment, 0, pdfBinaryComment.Length); - writer.WriteLine(); - - writer.Flush(); - xrefs.Add(stream.Position); - - writer.Write( - xrefs.Count.ToString() + " 0 obj\r\n" + - "<<\r\n" + - "/Type /Catalog\r\n" + - "/Pages 2 0 R\r\n" + - ">>\r\n" + - "endobj\r\n" - ); - - writer.Flush(); - xrefs.Add(stream.Position); - - writer.Write( - xrefs.Count.ToString() + " 0 obj\r\n" + - "<<\r\n" + - "/Count 1\r\n" + - "/Kids [ <<\r\n" + - "/Type /Page\r\n" + - "/Parent 2 0 R\r\n" + - "/MediaBox [0 0 " + pdfMediaSize + " " + pdfMediaSize + "]\r\n" + - "/Resources << /ProcSet [ /PDF /ImageC ]\r\n" + - "/XObject << /Im1 4 0 R >> >>\r\n" + - "/Contents 3 0 R\r\n" + - ">> ]\r\n" + - ">>\r\n" + - "endobj\r\n" - ); - - var X = "q\r\n" + - pdfMediaSize + " 0 0 " + pdfMediaSize + " 0 0 cm\r\n" + - "/Im1 Do\r\n" + - "Q"; - - writer.Flush(); - xrefs.Add(stream.Position); - - writer.Write( - xrefs.Count.ToString() + " 0 obj\r\n" + - "<< /Length " + X.Length.ToString() + " >>\r\n" + - "stream\r\n" + - X + "endstream\r\n" + - "endobj\r\n" - ); - - writer.Flush(); - xrefs.Add(stream.Position); - - writer.Write( - xrefs.Count.ToString() + " 0 obj\r\n" + - "<<\r\n" + - "/Name /Im1\r\n" + - "/Type /XObject\r\n" + - "/Subtype /Image\r\n" + - "/Width " + imgSize.ToString() + "/Height " + imgSize.ToString() + "/Length 5 0 R\r\n" + - "/Filter /DCTDecode\r\n" + - "/ColorSpace /DeviceRGB\r\n" + - "/BitsPerComponent 8\r\n" + - ">>\r\n" + - "stream\r\n" - ); - writer.Flush(); - stream.Write(jpgArray, 0, jpgArray.Length); - writer.Write( - "\r\n" + - "endstream\r\n" + - "endobj\r\n" - ); - - writer.Flush(); - xrefs.Add(stream.Position); - - writer.Write( - xrefs.Count.ToString() + " 0 obj\r\n" + - jpgArray.Length.ToString() + " endobj\r\n" - ); - - writer.Flush(); - var startxref = stream.Position; - - writer.Write( - "xref\r\n" + - "0 " + (xrefs.Count + 1).ToString() + "\r\n" + - "0000000000 65535 f\r\n" - ); - - foreach (var refValue in xrefs) - writer.Write(refValue.ToString("0000000000") + " 00000 n\r\n"); - - writer.Write( - "trailer\r\n" + - "<<\r\n" + - "/Size " + (xrefs.Count + 1).ToString() + "\r\n" + - "/Root 1 0 R\r\n" + - ">>\r\n" + - "startxref\r\n" + - startxref.ToString() + "\r\n" + - "%%EOF" - ); - - writer.Flush(); - - stream.Position = 0; - - return stream.ToArray(); - } + Param = new EncoderParameter[] { new EncoderParameter(Encoder.Quality, jpgQuality) } + }; + img.Save(msJpeg, jpgImageCodecInfo, jpgEncoderParameters); + jpgArray = msJpeg.ToArray(); } + + //Create PDF document + using var stream = new MemoryStream(); + var writer = new StreamWriter(stream, System.Text.Encoding.GetEncoding("ASCII")); + + var xrefs = new List(); + + writer.Write("%PDF-1.5\r\n"); + writer.Flush(); + + stream.Write(_pdfBinaryComment, 0, _pdfBinaryComment.Length); + writer.WriteLine(); + + writer.Flush(); + xrefs.Add(stream.Position); + + writer.Write( + xrefs.Count.ToString() + " 0 obj\r\n" + + "<<\r\n" + + "/Type /Catalog\r\n" + + "/Pages 2 0 R\r\n" + + ">>\r\n" + + "endobj\r\n" + ); + + writer.Flush(); + xrefs.Add(stream.Position); + + writer.Write( + xrefs.Count.ToString() + " 0 obj\r\n" + + "<<\r\n" + + "/Count 1\r\n" + + "/Kids [ <<\r\n" + + "/Type /Page\r\n" + + "/Parent 2 0 R\r\n" + + "/MediaBox [0 0 " + pdfMediaSize + " " + pdfMediaSize + "]\r\n" + + "/Resources << /ProcSet [ /PDF /ImageC ]\r\n" + + "/XObject << /Im1 4 0 R >> >>\r\n" + + "/Contents 3 0 R\r\n" + + ">> ]\r\n" + + ">>\r\n" + + "endobj\r\n" + ); + + var X = "q\r\n" + + pdfMediaSize + " 0 0 " + pdfMediaSize + " 0 0 cm\r\n" + + "/Im1 Do\r\n" + + "Q"; + + writer.Flush(); + xrefs.Add(stream.Position); + + writer.Write( + xrefs.Count.ToString() + " 0 obj\r\n" + + "<< /Length " + X.Length.ToString() + " >>\r\n" + + "stream\r\n" + + X + "endstream\r\n" + + "endobj\r\n" + ); + + writer.Flush(); + xrefs.Add(stream.Position); + + writer.Write( + xrefs.Count.ToString() + " 0 obj\r\n" + + "<<\r\n" + + "/Name /Im1\r\n" + + "/Type /XObject\r\n" + + "/Subtype /Image\r\n" + + "/Width " + imgSize.ToString() + "/Height " + imgSize.ToString() + "/Length 5 0 R\r\n" + + "/Filter /DCTDecode\r\n" + + "/ColorSpace /DeviceRGB\r\n" + + "/BitsPerComponent 8\r\n" + + ">>\r\n" + + "stream\r\n" + ); + writer.Flush(); + stream.Write(jpgArray, 0, jpgArray.Length); + writer.Write( + "\r\n" + + "endstream\r\n" + + "endobj\r\n" + ); + + writer.Flush(); + xrefs.Add(stream.Position); + + writer.Write( + xrefs.Count.ToString() + " 0 obj\r\n" + + jpgArray.Length.ToString() + " endobj\r\n" + ); + + writer.Flush(); + var startxref = stream.Position; + + writer.Write( + "xref\r\n" + + "0 " + (xrefs.Count + 1).ToString() + "\r\n" + + "0000000000 65535 f\r\n" + ); + + foreach (var refValue in xrefs) + writer.Write(refValue.ToString("0000000000") + " 00000 n\r\n"); + + writer.Write( + "trailer\r\n" + + "<<\r\n" + + "/Size " + (xrefs.Count + 1).ToString() + "\r\n" + + "/Root 1 0 R\r\n" + + ">>\r\n" + + "startxref\r\n" + + startxref.ToString() + "\r\n" + + "%%EOF" + ); + + writer.Flush(); + + stream.Position = 0; + + return stream.ToArray(); } +} #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] +[System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif +/// +/// Provides static methods for creating PDF byte array QR codes. +/// +public static class PdfByteQRCodeHelper +{ /// - /// Provides static methods for creating PDF byte array QR codes. + /// Creates a PDF byte array QR code with a single function call. /// - public static class PdfByteQRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Returns the QR code graphic as a PDF byte array. + public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, + string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, + EciMode eciMode = EciMode.Default, int requestedVersion = -1) { - /// - /// Creates a PDF byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Returns the QR code graphic as a PDF byte array. - public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, - string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, - EciMode eciMode = EciMode.Default, int requestedVersion = -1) - { - using (var qrGenerator = new QRCodeGenerator()) - using ( - var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, - requestedVersion)) - using (var qrCode = new PdfByteQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, + requestedVersion); + using var qrCode = new PdfByteQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex); + } - /// - /// Creates a PDF byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The level of error correction data. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a PDF byte array. - public static byte[] GetQRCode(string txt, ECCLevel eccLevel, int size) - { - using (var qrGen = new QRCodeGenerator()) - using (var qrCode = qrGen.CreateQrCode(txt, eccLevel)) - using (var qrBmp = new PdfByteQRCode(qrCode)) - return qrBmp.GetGraphic(size); + /// + /// Creates a PDF byte array QR code with a single function call. + /// + /// The text or payload to be encoded inside the QR code. + /// The level of error correction data. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a PDF byte array. + public static byte[] GetQRCode(string txt, ECCLevel eccLevel, int size) + { + using var qrGen = new QRCodeGenerator(); + using var qrCode = qrGen.CreateQrCode(txt, eccLevel); + using var qrBmp = new PdfByteQRCode(qrCode); + return qrBmp.GetGraphic(size); - } } } #endif diff --git a/QRCoder/PngByteQRCode.cs b/QRCoder/PngByteQRCode.cs index 757b8e05..8fa69f95 100644 --- a/QRCoder/PngByteQRCode.cs +++ b/QRCoder/PngByteQRCode.cs @@ -1,385 +1,376 @@ -using System; +using System; using System.IO; using System.IO.Compression; using static QRCoder.QRCodeGenerator; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents a QR code generator that outputs QR codes as PNG byte arrays. +/// +public sealed class PngByteQRCode : AbstractQRCode, IDisposable { /// - /// Represents a QR code generator that outputs QR codes as PNG byte arrays. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. /// - public sealed class PngByteQRCode : AbstractQRCode, IDisposable + public PngByteQRCode() { } + + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public PngByteQRCode(QRCodeData data) : base(data) { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public PngByteQRCode() { } + } - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public PngByteQRCode(QRCodeData data) : base(data) - { - } + /// + /// Creates a black and white PNG of the QR code, using 1-bit grayscale. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a PNG byte array. + public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true) + { + using var png = new PngBuilder(); + var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; + png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale); + png.WriteScanlines(DrawScanlines(pixelsPerModule, drawQuietZones)); + png.WriteEnd(); + return png.GetBytes(); + } - /// - /// Creates a black and white PNG of the QR code, using 1-bit grayscale. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a PNG byte array. - public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true) - { - using (var png = new PngBuilder()) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale); - png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones)); - png.WriteEnd(); - return png.GetBytes(); - } - } + /// + /// Creates a 2-color PNG of the QR code, using 1-bit indexed color. Accepts 3-byte RGB colors for normal images and 4-byte RGBA-colors for transparent images. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules as an RGBA byte array. + /// The color of the light modules as an RGBA byte array. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a PNG byte array. + public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, bool drawQuietZones = true) + { + using var png = new PngBuilder(); + var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; + png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed); + png.WritePalette(darkColorRgba, lightColorRgba); + png.WriteScanlines(DrawScanlines(pixelsPerModule, drawQuietZones)); + png.WriteEnd(); + return png.GetBytes(); + } - /// - /// Creates a 2-color PNG of the QR code, using 1-bit indexed color. Accepts 3-byte RGB colors for normal images and 4-byte RGBA-colors for transparent images. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules as an RGBA byte array. - /// The color of the light modules as an RGBA byte array. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a PNG byte array. - public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, bool drawQuietZones = true) - { - using (var png = new PngBuilder()) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed); - png.WritePalette(darkColorRgba, lightColorRgba); - png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones)); - png.WriteEnd(); - return png.GetBytes(); - } - } + /// + /// Creates a bitmap where each pixel is represented by a single bit, dark = 0 and light = 1. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the bitmap as a byte array. + private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones) + { + var moduleMatrix = QrCodeData.ModuleMatrix; + var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8); + var quietZoneOffset = (drawQuietZones ? 0 : 4); + var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel. + var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule]; - /// - /// Creates a bitmap where each pixel is represented by a single bit, dark = 0 and light = 1. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the bitmap as a byte array. - private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones) + for (var y = 0; y < matrixSize; y++) { - var moduleMatrix = this.QrCodeData.ModuleMatrix; - var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8); - var quietZoneOffset = (drawQuietZones ? 0 : 4); - var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel. - var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule]; + var modules = moduleMatrix[y + quietZoneOffset]; + var scanlineOffset = y * pixelsPerModule * bytesPerScanline; - for (var y = 0; y < matrixSize; y++) + // Draw a scanline with the modules from the QR code. + for (var x = 0; x < matrixSize; x++) { - var modules = moduleMatrix[y+quietZoneOffset]; - var scanlineOffset = y * pixelsPerModule * bytesPerScanline; - - // Draw a scanline with the modules from the QR code. - for (var x = 0; x < matrixSize; x++) + if (modules[x + quietZoneOffset]) { - if (modules[x + quietZoneOffset]) - { - continue; - } - - var pixelIndex = x * pixelsPerModule; - var endIndex = pixelIndex + pixelsPerModule; - for (; pixelIndex < endIndex; pixelIndex++) - { - scanlines[scanlineOffset + 1 + pixelIndex / 8] |= (byte)(0x80 >> (pixelIndex % 8)); - } + continue; } - // Copy the scanline the required number of times. - for (var copyCount = 1; copyCount < pixelsPerModule; copyCount++) + var pixelIndex = x * pixelsPerModule; + var endIndex = pixelIndex + pixelsPerModule; + for (; pixelIndex < endIndex; pixelIndex++) { - Array.Copy(scanlines, scanlineOffset, scanlines, scanlineOffset + copyCount * bytesPerScanline, bytesPerScanline); + scanlines[scanlineOffset + 1 + pixelIndex / 8] |= (byte)(0x80 >> (pixelIndex % 8)); } } - return scanlines; + // Copy the scanline the required number of times. + for (var copyCount = 1; copyCount < pixelsPerModule; copyCount++) + { + Array.Copy(scanlines, scanlineOffset, scanlines, scanlineOffset + copyCount * bytesPerScanline, bytesPerScanline); + } } - /// - /// Writes the chunks that make up a PNG file. - /// - /// - /// See https://www.w3.org/TR/2003/REC-PNG-20031110 and https://www.ietf.org/rfc/rfc1950.txt. - /// - private sealed class PngBuilder : IDisposable - { - private static readonly byte[] PngSignature = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; + return scanlines; + } - private static readonly uint[] CrcTable = { - 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 - }; + /// + /// Writes the chunks that make up a PNG file. + /// + /// + /// See https://www.w3.org/TR/2003/REC-PNG-20031110 and https://www.ietf.org/rfc/rfc1950.txt. + /// + private sealed class PngBuilder : IDisposable + { + private static readonly byte[] _pngSignature = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; - // ReSharper disable InconsistentNaming - // Chunk types - private static readonly byte[] IHDR = { 73, 72, 68, 82 }; + private static readonly uint[] _crcTable = { + 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 + }; - private static readonly byte[] IDAT = { 73, 68, 65, 84 }; + // ReSharper disable InconsistentNaming + // Chunk types + private static readonly byte[] _ihdr = { 73, 72, 68, 82 }; - private static readonly byte[] IEND = { 73, 69, 78, 68 }; + private static readonly byte[] _idat = { 73, 68, 65, 84 }; - private static readonly byte[] PLTE = { 80, 76, 84, 69 }; + private static readonly byte[] _iend = { 73, 69, 78, 68 }; - private static readonly byte[] tRNS = { 116, 82, 78, 83 }; - // ReSharper enable InconsistentNaming + private static readonly byte[] _plte = { 80, 76, 84, 69 }; - public enum ColorType : byte - { - Greyscale = 0, - Indexed = 3 - } + private static readonly byte[] _trns = { 116, 82, 78, 83 }; + // ReSharper enable InconsistentNaming - private MemoryStream stream = new MemoryStream(); + public enum ColorType : byte + { + Greyscale = 0, + Indexed = 3 + } - public void Dispose() - { - this.stream?.Dispose(); - this.stream = null!; - } + private MemoryStream _stream = new MemoryStream(); - public byte[] GetBytes() - { - var bytes = this.stream.ToArray(); + public void Dispose() + { + _stream?.Dispose(); + _stream = null!; + } - // Enumerate chunks in file and insert their CRC32 checksums. - var chunkOffset = PngSignature.Length; - while (chunkOffset < bytes.Length) - { - // Read length field. - var dataLength = (bytes[chunkOffset] << 24) | (bytes[chunkOffset + 1] << 16) | (bytes[chunkOffset + 2] << 8) | bytes[chunkOffset + 3]; + public byte[] GetBytes() + { + var bytes = _stream.ToArray(); - // CRC is computed from type and data fields. - var crc = Crc32(bytes, chunkOffset + 4, dataLength + 4); + // Enumerate chunks in file and insert their CRC32 checksums. + var chunkOffset = _pngSignature.Length; + while (chunkOffset < bytes.Length) + { + // Read length field. + var dataLength = (bytes[chunkOffset] << 24) | (bytes[chunkOffset + 1] << 16) | (bytes[chunkOffset + 2] << 8) | bytes[chunkOffset + 3]; - // Write CRC to end of chunk. - var crcOffset = chunkOffset + 8 + dataLength; - bytes[crcOffset + 0] = (byte)(crc >> 24); - bytes[crcOffset + 1] = (byte)(crc >> 16); - bytes[crcOffset + 2] = (byte)(crc >> 8); - bytes[crcOffset + 3] = (byte)crc; + // CRC is computed from type and data fields. + var crc = Crc32(bytes, chunkOffset + 4, dataLength + 4); - // Seek to next chunk. - chunkOffset = crcOffset + 4; - } + // Write CRC to end of chunk. + var crcOffset = chunkOffset + 8 + dataLength; + bytes[crcOffset + 0] = (byte)(crc >> 24); + bytes[crcOffset + 1] = (byte)(crc >> 16); + bytes[crcOffset + 2] = (byte)(crc >> 8); + bytes[crcOffset + 3] = (byte)crc; - return bytes; + // Seek to next chunk. + chunkOffset = crcOffset + 4; } - /// - /// Writes the IHDR chunk. This must be the first chunk in the file. - /// - public void WriteHeader(int width, int height, byte bitDepth, ColorType colorType) - { - this.stream.Write(PngSignature, 0, PngSignature.Length); - this.WriteChunkStart(IHDR, 13); + return bytes; + } - // Size. - this.WriteIntBigEndian((uint)width); - this.WriteIntBigEndian((uint)height); + /// + /// Writes the IHDR chunk. This must be the first chunk in the file. + /// + public void WriteHeader(int width, int height, byte bitDepth, ColorType colorType) + { + _stream.Write(_pngSignature, 0, _pngSignature.Length); + WriteChunkStart(_ihdr, 13); - // Color. - this.stream.WriteByte(bitDepth); - this.stream.WriteByte((byte)colorType); + // Size. + WriteIntBigEndian((uint)width); + WriteIntBigEndian((uint)height); - // Constants. - this.stream.WriteByte(0); - this.stream.WriteByte(0); - this.stream.WriteByte(0); + // Color. + _stream.WriteByte(bitDepth); + _stream.WriteByte((byte)colorType); - this.WriteChunkEnd(); - } + // Constants. + _stream.WriteByte(0); + _stream.WriteByte(0); + _stream.WriteByte(0); - /// - /// Writes the PLTE chunk, and also the tRNS chunk if necessary. Must come before the IDAT chunk. - /// - public void WritePalette(params byte[][] rgbaColors) - { - const int Red = 0, Green = 1, Blue = 2, Alpha = 3; - const byte Opaque = 255; - var hasAlpha = false; + WriteChunkEnd(); + } - this.WriteChunkStart(PLTE, 3 * rgbaColors.Length); - foreach (var color in rgbaColors) - { - hasAlpha |= color.Length > Alpha && color[Alpha] < Opaque; - this.stream.WriteByte(color[Red]); - this.stream.WriteByte(color[Green]); - this.stream.WriteByte(color[Blue]); - } - this.WriteChunkEnd(); + /// + /// Writes the PLTE chunk, and also the tRNS chunk if necessary. Must come before the IDAT chunk. + /// + public void WritePalette(params byte[][] rgbaColors) + { + const int Red = 0, Green = 1, Blue = 2, Alpha = 3; + const byte Opaque = 255; + var hasAlpha = false; - if (!hasAlpha) - { - return; - } + WriteChunkStart(_plte, 3 * rgbaColors.Length); + foreach (var color in rgbaColors) + { + hasAlpha |= color.Length > Alpha && color[Alpha] < Opaque; + _stream.WriteByte(color[Red]); + _stream.WriteByte(color[Green]); + _stream.WriteByte(color[Blue]); + } + WriteChunkEnd(); - this.WriteChunkStart(tRNS, rgbaColors.Length); - foreach (var color in rgbaColors) - { - this.stream.WriteByte(color.Length > Alpha ? color[Alpha] : Opaque); - } - this.WriteChunkEnd(); + if (!hasAlpha) + { + return; } - /// - /// Writes the IDAT chunk with the actual picture. - /// - public void WriteScanlines(byte[] scanlines) + WriteChunkStart(_trns, rgbaColors.Length); + foreach (var color in rgbaColors) { - using (var idatStream = new MemoryStream()) - { - Deflate(idatStream, scanlines); + _stream.WriteByte(color.Length > Alpha ? color[Alpha] : Opaque); + } + WriteChunkEnd(); + } - this.WriteChunkStart(IDAT, (int)(idatStream.Length + 6)); + /// + /// Writes the IDAT chunk with the actual picture. + /// + public void WriteScanlines(byte[] scanlines) + { + using var idatStream = new MemoryStream(); + Deflate(idatStream, scanlines); + + WriteChunkStart(_idat, (int)(idatStream.Length + 6)); - // Deflate header. - this.stream.WriteByte(0x78); // 8 Deflate algorithm, 7 max window size - this.stream.WriteByte(0x9C); // Check bits. + // Deflate header. + _stream.WriteByte(0x78); // 8 Deflate algorithm, 7 max window size + _stream.WriteByte(0x9C); // Check bits. - // Compressed data. - idatStream.Position = 0; + // Compressed data. + idatStream.Position = 0; #if NET35 - idatStream.WriteTo(this.stream); + idatStream.WriteTo(_stream); #else - idatStream.CopyTo(this.stream); + idatStream.CopyTo(_stream); #endif - // Deflate checksum. - var adler = Adler32(scanlines, 0, scanlines.Length); - this.WriteIntBigEndian(adler); + // Deflate checksum. + var adler = Adler32(scanlines, 0, scanlines.Length); + WriteIntBigEndian(adler); - this.WriteChunkEnd(); - } - } - - /// - /// Writes the IEND chunk. This must be the last chunk in the file. - /// - public void WriteEnd() - { - this.WriteChunkStart(IEND, 0); - this.WriteChunkEnd(); - } + WriteChunkEnd(); + } - private void WriteChunkStart(byte[] type, int length) - { - this.WriteIntBigEndian((uint)length); - this.stream.Write(type, 0, 4); - } + /// + /// Writes the IEND chunk. This must be the last chunk in the file. + /// + public void WriteEnd() + { + WriteChunkStart(_iend, 0); + WriteChunkEnd(); + } - private void WriteChunkEnd() - { - // Reserves 4 bytes space for crc32 so GetBytes can add it later. - this.stream.SetLength(this.stream.Length + 4); - this.stream.Position += 4; - } + private void WriteChunkStart(byte[] type, int length) + { + WriteIntBigEndian((uint)length); + _stream.Write(type, 0, 4); + } - private void WriteIntBigEndian(uint value) - { - this.stream.WriteByte((byte)(value >> 24)); - this.stream.WriteByte((byte)(value >> 16)); - this.stream.WriteByte((byte)(value >> 8)); - this.stream.WriteByte((byte)value); - } + private void WriteChunkEnd() + { + // Reserves 4 bytes space for crc32 so GetBytes can add it later. + _stream.SetLength(_stream.Length + 4); + _stream.Position += 4; + } - private static void Deflate(Stream output, byte[] bytes) - { - using (var deflateStream = new DeflateStream(output, CompressionMode.Compress, leaveOpen: true)) - { - deflateStream.Write(bytes, 0, bytes.Length); - } - } + private void WriteIntBigEndian(uint value) + { + _stream.WriteByte((byte)(value >> 24)); + _stream.WriteByte((byte)(value >> 16)); + _stream.WriteByte((byte)(value >> 8)); + _stream.WriteByte((byte)value); + } - // Reference implementation from RFC 1950. Not optimized. - private static uint Adler32(byte[] data, int index, int length) - { - const uint Base = 65521; - uint s1 = 1, s2 = 0; + private static void Deflate(Stream output, byte[] bytes) + { + using var deflateStream = new DeflateStream(output, CompressionMode.Compress, leaveOpen: true); + deflateStream.Write(bytes, 0, bytes.Length); + } - var end = index + length; - for (var n = index; n < end; n++) - { - s1 = (s1 + data[n]) % Base; - s2 = (s2 + s1) % Base; - } + // Reference implementation from RFC 1950. Not optimized. + private static uint Adler32(byte[] data, int index, int length) + { + const uint Base = 65521; + uint s1 = 1, s2 = 0; - return (s2 << 16) + s1; + var end = index + length; + for (var n = index; n < end; n++) + { + s1 = (s1 + data[n]) % Base; + s2 = (s2 + s1) % Base; } - // Reference implementation from REC-PNG-20031110. Not optimized. - private static uint Crc32(byte[] data, int index, int length) - { - var c = 0xffffffff; + return (s2 << 16) + s1; + } - var end = index + length; - for (var n = index; n < end; n++) - { - c = CrcTable[(c ^ data[n]) & 0xff] ^ (c >> 8); - } + // Reference implementation from REC-PNG-20031110. Not optimized. + private static uint Crc32(byte[] data, int index, int length) + { + var c = 0xffffffff; - return c ^ 0xffffffff; + var end = index + length; + for (var n = index; n < end; n++) + { + c = _crcTable[(c ^ data[n]) & 0xff] ^ (c >> 8); } + + return c ^ 0xffffffff; } } +} +/// +/// Provides static methods for creating PNG byte array QR codes. +/// +public static class PngByteQRCodeHelper +{ /// - /// Provides static methods for creating PNG byte array QR codes. + /// Creates a PNG byte array QR code with a single function call. /// - public static class PngByteQRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules as an RGBA byte array. + /// The color of the light modules as an RGBA byte array. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a PNG byte array. + public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true) { - /// - /// Creates a PNG byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules as an RGBA byte array. - /// The color of the light modules as an RGBA byte array. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a PNG byte array. - public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new PngByteQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba, drawQuietZones); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new PngByteQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba, drawQuietZones); + } - /// - /// Creates a PNG byte array QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The level of error correction data. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a PNG byte array. - public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true) - { - using (var qrGen = new QRCodeGenerator()) - using (var qrCode = qrGen.CreateQrCode(txt, eccLevel)) - using (var qrPng = new PngByteQRCode(qrCode)) - return qrPng.GetGraphic(size, drawQuietZones); - } + /// + /// Creates a PNG byte array QR code with a single function call. + /// + /// The text or payload to be encoded inside the QR code. + /// The level of error correction data. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a PNG byte array. + public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true) + { + using var qrGen = new QRCodeGenerator(); + using var qrCode = qrGen.CreateQrCode(txt, eccLevel); + using var qrPng = new PngByteQRCode(qrCode); + return qrPng.GetGraphic(size, drawQuietZones); } } diff --git a/QRCoder/PostscriptQRCode.cs b/QRCoder/PostscriptQRCode.cs index 97d09a19..2e36bc76 100644 --- a/QRCoder/PostscriptQRCode.cs +++ b/QRCoder/PostscriptQRCode.cs @@ -1,145 +1,138 @@ - + #if !NETSTANDARD1_3 using System; using System.Drawing; using static QRCoder.QRCodeGenerator; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents a QR code generator that outputs QR codes as PostScript code. +/// +public class PostscriptQRCode : AbstractQRCode, IDisposable { /// - /// Represents a QR code generator that outputs QR codes as PostScript code. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. + /// + public PostscriptQRCode() { } + + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public PostscriptQRCode(QRCodeData data) : base(data) { } + + /// + /// Creates a black and white PostScript code representation of the QR code. /// - public class PostscriptQRCode : AbstractQRCode, IDisposable + /// The number of points each dark/light module of the QR code will occupy in the final QR code image. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(int pointsPerModule, bool epsFormat = false) { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public PostscriptQRCode() { } - - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public PostscriptQRCode(QRCodeData data) : base(data) { } - - /// - /// Creates a black and white PostScript code representation of the QR code. - /// - /// The number of points each dark/light module of the QR code will occupy in the final QR code image. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(int pointsPerModule, bool epsFormat = false) - { - var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count); - return this.GetGraphic(viewBox, Color.Black, Color.White, true, epsFormat); - } + var viewBox = new Size(pointsPerModule * QrCodeData.ModuleMatrix.Count, pointsPerModule * QrCodeData.ModuleMatrix.Count); + return GetGraphic(viewBox, Color.Black, Color.White, true, epsFormat); + } - /// - /// Creates a colored PostScript code representation of the QR code. - /// - /// The number of points each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(int pointsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false) - { - var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count); - return this.GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, epsFormat); - } + /// + /// Creates a colored PostScript code representation of the QR code. + /// + /// The number of points each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(int pointsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false) + { + var viewBox = new Size(pointsPerModule * QrCodeData.ModuleMatrix.Count, pointsPerModule * QrCodeData.ModuleMatrix.Count); + return GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, epsFormat); + } - /// - /// Creates a colored PostScript code representation of the QR code. - /// - /// The number of points each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(int pointsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false) - { - var viewBox = new Size(pointsPerModule * this.QrCodeData.ModuleMatrix.Count, pointsPerModule * this.QrCodeData.ModuleMatrix.Count); - return this.GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, epsFormat); - } + /// + /// Creates a colored PostScript code representation of the QR code. + /// + /// The number of points each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(int pointsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false) + { + var viewBox = new Size(pointsPerModule * QrCodeData.ModuleMatrix.Count, pointsPerModule * QrCodeData.ModuleMatrix.Count); + return GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, epsFormat); + } - /// - /// Creates a black and white PostScript code representation of the QR code. - /// - /// The dimensions of the viewbox for the QR code. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(Size viewBox, bool drawQuietZones = true, bool epsFormat = false) - { - return this.GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, epsFormat); - } + /// + /// Creates a black and white PostScript code representation of the QR code. + /// + /// The dimensions of the viewbox for the QR code. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(Size viewBox, bool drawQuietZones = true, bool epsFormat = false) + => GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, epsFormat); - /// - /// Creates a colored PostScript code representation of the QR code. - /// - /// The dimensions of the viewbox for the QR code. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false) - { - return this.GetGraphic(viewBox, ColorTranslator.FromHtml(darkColorHex), ColorTranslator.FromHtml(lightColorHex), drawQuietZones, epsFormat); - } + /// + /// Creates a colored PostScript code representation of the QR code. + /// + /// The dimensions of the viewbox for the QR code. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, bool epsFormat = false) + => GetGraphic(viewBox, ColorTranslator.FromHtml(darkColorHex), ColorTranslator.FromHtml(lightColorHex), drawQuietZones, epsFormat); + + /// + /// Creates a colored PostScript code representation of the QR code. + /// + /// The dimensions of the viewbox for the QR code. + /// The color of the dark modules. + /// The color of the light modules. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false) + { + var offset = drawQuietZones ? 0 : 4; + var drawableModulesCount = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); + var pointsPerModule = (double)Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; - /// - /// Creates a colored PostScript code representation of the QR code. - /// - /// The dimensions of the viewbox for the QR code. - /// The color of the dark modules. - /// The color of the light modules. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, bool epsFormat = false) + string psFile = string.Format(PS_HEADER, new object[] { + DateTime.Now.ToString("s"), CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule), + epsFormat ? "EPSF-3.0" : string.Empty + }); + psFile += string.Format(PS_FUNCTIONS, new object[] { + CleanSvgVal(darkColor.R /255.0), CleanSvgVal(darkColor.G /255.0), CleanSvgVal(darkColor.B /255.0), + CleanSvgVal(lightColor.R /255.0), CleanSvgVal(lightColor.G /255.0), CleanSvgVal(lightColor.B /255.0), + drawableModulesCount + }); + + for (int xi = offset; xi < offset + drawableModulesCount; xi++) { - var offset = drawQuietZones ? 0 : 4; - var drawableModulesCount = this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); - var pointsPerModule = (double)Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; - - string psFile = string.Format(psHeader, new object[] { - DateTime.Now.ToString("s"), CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule), - epsFormat ? "EPSF-3.0" : string.Empty - }); - psFile += string.Format(psFunctions, new object[] { - CleanSvgVal(darkColor.R /255.0), CleanSvgVal(darkColor.G /255.0), CleanSvgVal(darkColor.B /255.0), - CleanSvgVal(lightColor.R /255.0), CleanSvgVal(lightColor.G /255.0), CleanSvgVal(lightColor.B /255.0), - drawableModulesCount - }); - - for (int xi = offset; xi < offset + drawableModulesCount; xi++) + if (xi > offset) + psFile += "nl\n"; + for (int yi = offset; yi < offset + drawableModulesCount; yi++) { - if (xi > offset) - psFile += "nl\n"; - for (int yi = offset; yi < offset + drawableModulesCount; yi++) - { - psFile += (this.QrCodeData.ModuleMatrix[xi][yi] ? "f " : "b "); - } - psFile += "\n"; + psFile += (QrCodeData.ModuleMatrix[xi][yi] ? "f " : "b "); } - return psFile + psFooter; + psFile += "\n"; } + return psFile + PS_FOOTER; + } - /// - /// Cleans double values for international use/formats. - /// - /// The input double value. - /// Returns the cleaned string representation of the double value. - private string CleanSvgVal(double input) - { - return input.ToString(System.Globalization.CultureInfo.InvariantCulture); - } + /// + /// Cleans double values for international use/formats. + /// + /// The input double value. + /// Returns the cleaned string representation of the double value. + private string CleanSvgVal(double input) => input.ToString(System.Globalization.CultureInfo.InvariantCulture); - private const string psHeader = @"%!PS-Adobe-3.0 {3} + private const string PS_HEADER = @"%!PS-Adobe-3.0 {3} %%Creator: QRCoder.NET %%Title: QRCode %%CreationDate: {0} @@ -160,7 +153,7 @@ private string CleanSvgVal(double input) %%EndFeature "; - private const string psFunctions = @"%%BeginFunctions + private const string PS_FUNCTIONS = @"%%BeginFunctions /csquare {{ newpath 0 0 moveto @@ -196,40 +189,39 @@ sc sc scale 0 {6} 1 sub translate "; - private const string psFooter = @"%%EndBody + private const string PS_FOOTER = @"%%EndBody grestore showpage %%EOF "; - } +} +/// +/// Provides static methods for creating PostScript QR codes. +/// +public static class PostscriptQRCodeHelper +{ /// - /// Provides static methods for creating PostScript QR codes. + /// Creates a PostScript QR code with a single function call. /// - public static class PostscriptQRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of points each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Indicates if quiet zones around the QR code should be drawn. + /// Indicates if the output should be in EPS format. + /// Returns the QR code graphic as a PostScript string. + public static string GetQRCode(string plainText, int pointsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, bool epsFormat = false) { - /// - /// Creates a PostScript QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of points each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Indicates if quiet zones around the QR code should be drawn. - /// Indicates if the output should be in EPS format. - /// Returns the QR code graphic as a PostScript string. - public static string GetQRCode(string plainText, int pointsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, bool epsFormat = false) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new PostscriptQRCode(qrCodeData)) - return qrCode.GetGraphic(pointsPerModule, darkColorHex, lightColorHex, drawQuietZones, epsFormat); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new PostscriptQRCode(qrCodeData); + return qrCode.GetGraphic(pointsPerModule, darkColorHex, lightColorHex, drawQuietZones, epsFormat); } } #endif diff --git a/QRCoder/Properties/AssemblyInfo.cs b/QRCoder/Properties/AssemblyInfo.cs index 86eade36..35379fae 100644 --- a/QRCoder/Properties/AssemblyInfo.cs +++ b/QRCoder/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +using System.Reflection; using System.Runtime.InteropServices; // Allgemeine Informationen über eine Assembly werden über die folgenden diff --git a/QRCoder/QRCode.cs b/QRCoder/QRCode.cs index 74f501bf..8b312746 100644 --- a/QRCoder/QRCode.cs +++ b/QRCoder/QRCode.cs @@ -4,209 +4,202 @@ using System.Drawing.Drawing2D; using static QRCoder.QRCodeGenerator; -namespace QRCoder -{ +namespace QRCoder; + #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] +[System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif +/// +/// Represents a QR code generator that outputs QR codes as bitmap images. +/// +public class QRCode : AbstractQRCode, IDisposable +{ /// - /// Represents a QR code generator that outputs QR codes as bitmap images. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. /// - public class QRCode : AbstractQRCode, IDisposable - { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public QRCode() { } - - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public QRCode(QRCodeData data) : base(data) { } - - /// - /// Creates a black and white bitmap image of the QR code. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// Returns the QR code graphic as a bitmap. - public Bitmap GetGraphic(int pixelsPerModule) - { - return this.GetGraphic(pixelsPerModule, Color.Black, Color.White, true); - } + public QRCode() { } - /// - /// Creates a colored bitmap image of the QR code. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HTML hex format. - /// The color of the light modules in HTML hex format. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a bitmap. - public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true) - { - return this.GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones); - } + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public QRCode(QRCodeData data) : base(data) { } - /// - /// Creates a colored bitmap image of the QR code. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a bitmap. - public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; + /// + /// Creates a black and white bitmap image of the QR code. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// Returns the QR code graphic as a bitmap. + public Bitmap GetGraphic(int pixelsPerModule) + => GetGraphic(pixelsPerModule, Color.Black, Color.White, true); - var bmp = new Bitmap(size, size); - using (var gfx = Graphics.FromImage(bmp)) - using (var lightBrush = new SolidBrush(lightColor)) - using (var darkBrush = new SolidBrush(darkColor)) + /// + /// Creates a colored bitmap image of the QR code. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HTML hex format. + /// The color of the light modules in HTML hex format. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a bitmap. + public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true) + => GetGraphic(pixelsPerModule, ColorTranslator.FromHtml(darkColorHtmlHex), ColorTranslator.FromHtml(lightColorHtmlHex), drawQuietZones); + + /// + /// Creates a colored bitmap image of the QR code. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a bitmap. + public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true) + { + var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; + var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; + + var bmp = new Bitmap(size, size); + using (var gfx = Graphics.FromImage(bmp)) + using (var lightBrush = new SolidBrush(lightColor)) + using (var darkBrush = new SolidBrush(darkColor)) + { + for (var x = 0; x < size + offset; x += pixelsPerModule) { - for (var x = 0; x < size + offset; x = x + pixelsPerModule) + for (var y = 0; y < size + offset; y += pixelsPerModule) { - for (var y = 0; y < size + offset; y = y + pixelsPerModule) + var module = QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1]; + + if (module) + { + gfx.FillRectangle(darkBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); + } + else { - var module = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1]; - - if (module) - { - gfx.FillRectangle(darkBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } - else - { - gfx.FillRectangle(lightBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } + gfx.FillRectangle(lightBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); } } - - gfx.Save(); } - return bmp; + gfx.Save(); } - /// - /// Creates a colored bitmap image of the QR code with an optional icon in the center. - /// - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// An optional icon to be placed in the center of the QR code. - /// The size of the icon as a percentage of the QR code size. - /// The width of the border around the icon. - /// Indicates if quiet zones around the QR code should be drawn. - /// The background color of the icon. - /// Returns the QR code graphic as a bitmap. - public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap? icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true, Color? iconBackgroundColor = null) - { - var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; + return bmp; + } - var bmp = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + /// + /// Creates a colored bitmap image of the QR code with an optional icon in the center. + /// + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// An optional icon to be placed in the center of the QR code. + /// The size of the icon as a percentage of the QR code size. + /// The width of the border around the icon. + /// Indicates if quiet zones around the QR code should be drawn. + /// The background color of the icon. + /// Returns the QR code graphic as a bitmap. + public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Bitmap? icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true, Color? iconBackgroundColor = null) + { + var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; + var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; - using (var gfx = Graphics.FromImage(bmp)) - using (var lightBrush = new SolidBrush(lightColor)) - using (var darkBrush = new SolidBrush(darkColor)) - { - gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; - gfx.CompositingQuality = CompositingQuality.HighQuality; - gfx.Clear(lightColor); - var drawIconFlag = icon != null && iconSizePercent > 0 && iconSizePercent <= 100; + var bmp = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + using (var gfx = Graphics.FromImage(bmp)) + using (var lightBrush = new SolidBrush(lightColor)) + using (var darkBrush = new SolidBrush(darkColor)) + { + gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; + gfx.CompositingQuality = CompositingQuality.HighQuality; + gfx.Clear(lightColor); + var drawIconFlag = icon != null && iconSizePercent > 0 && iconSizePercent <= 100; - for (var x = 0; x < size + offset; x = x + pixelsPerModule) + for (var x = 0; x < size + offset; x += pixelsPerModule) + { + for (var y = 0; y < size + offset; y += pixelsPerModule) { - for (var y = 0; y < size + offset; y = y + pixelsPerModule) - { - var moduleBrush = this.QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1] ? darkBrush : lightBrush; - gfx.FillRectangle(moduleBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } + var moduleBrush = QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1] ? darkBrush : lightBrush; + gfx.FillRectangle(moduleBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); } + } - if (drawIconFlag) + if (drawIconFlag) + { + float iconDestWidth = iconSizePercent * bmp.Width / 100f; + float iconDestHeight = drawIconFlag ? iconDestWidth * icon!.Height / icon.Width : 0; + float iconX = (bmp.Width - iconDestWidth) / 2; + float iconY = (bmp.Height - iconDestHeight) / 2; + var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + iconBorderWidth * 2, iconDestHeight + iconBorderWidth * 2); + var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight); + var iconBgBrush = iconBackgroundColor != null ? new SolidBrush((Color)iconBackgroundColor) : lightBrush; + //Only render icon/logo background, if iconBorderWith is set > 0 + if (iconBorderWidth > 0) { - float iconDestWidth = iconSizePercent * bmp.Width / 100f; - float iconDestHeight = drawIconFlag ? iconDestWidth * icon!.Height / icon.Width : 0; - float iconX = (bmp.Width - iconDestWidth) / 2; - float iconY = (bmp.Height - iconDestHeight) / 2; - var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + iconBorderWidth * 2, iconDestHeight + iconBorderWidth * 2); - var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight); - var iconBgBrush = iconBackgroundColor != null ? new SolidBrush((Color)iconBackgroundColor) : lightBrush; - //Only render icon/logo background, if iconBorderWith is set > 0 - if (iconBorderWidth > 0) - { - using (GraphicsPath iconPath = CreateRoundedRectanglePath(centerDest, iconBorderWidth * 2)) - { - gfx.FillPath(iconBgBrush, iconPath); - } - } - gfx.DrawImage(icon!, iconDestRect, new RectangleF(0, 0, icon!.Width, icon.Height), GraphicsUnit.Pixel); + using var iconPath = CreateRoundedRectanglePath(centerDest, iconBorderWidth * 2); + gfx.FillPath(iconBgBrush, iconPath); } - - gfx.Save(); + gfx.DrawImage(icon!, iconDestRect, new RectangleF(0, 0, icon!.Width, icon.Height), GraphicsUnit.Pixel); } - return bmp; + gfx.Save(); } - /// - /// Creates a rounded rectangle path for drawing. - /// - /// The rectangle for which to create the rounded path. - /// The radius of the corners. - /// Returns the rounded rectangle path. - internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius) - { - var roundedRect = new GraphicsPath(); - roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90); - roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y); - roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90); - roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2); - roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90); - roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom); - roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90); - roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2); - roundedRect.CloseFigure(); - return roundedRect; - } + return bmp; } + /// + /// Creates a rounded rectangle path for drawing. + /// + /// The rectangle for which to create the rounded path. + /// The radius of the corners. + /// Returns the rounded rectangle path. + internal GraphicsPath CreateRoundedRectanglePath(RectangleF rect, int cornerRadius) + { + var roundedRect = new GraphicsPath(); + roundedRect.AddArc(rect.X, rect.Y, cornerRadius * 2, cornerRadius * 2, 180, 90); + roundedRect.AddLine(rect.X + cornerRadius, rect.Y, rect.Right - cornerRadius * 2, rect.Y); + roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y, cornerRadius * 2, cornerRadius * 2, 270, 90); + roundedRect.AddLine(rect.Right, rect.Y + cornerRadius * 2, rect.Right, rect.Y + rect.Height - cornerRadius * 2); + roundedRect.AddArc(rect.X + rect.Width - cornerRadius * 2, rect.Y + rect.Height - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 0, 90); + roundedRect.AddLine(rect.Right - cornerRadius * 2, rect.Bottom, rect.X + cornerRadius * 2, rect.Bottom); + roundedRect.AddArc(rect.X, rect.Bottom - cornerRadius * 2, cornerRadius * 2, cornerRadius * 2, 90, 90); + roundedRect.AddLine(rect.X, rect.Bottom - cornerRadius * 2, rect.X, rect.Y + cornerRadius * 2); + roundedRect.CloseFigure(); + return roundedRect; + } +} + #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] +[System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif +/// +/// Provides static methods for creating bitmap QR codes. +/// +public static class QRCodeHelper +{ /// - /// Provides static methods for creating bitmap QR codes. + /// Creates a bitmap QR code with a single function call. /// - public static class QRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules. + /// The color of the light modules. + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// An optional icon to be placed in the center of the QR code. + /// The size of the icon as a percentage of the QR code size. + /// The width of the border around the icon. + /// Indicates if quiet zones around the QR code should be drawn. + /// Returns the QR code graphic as a bitmap. + public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap? icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true) { - /// - /// Creates a bitmap QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The number of pixels each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules. - /// The color of the light modules. - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// An optional icon to be placed in the center of the QR code. - /// The size of the icon as a percentage of the QR code size. - /// The width of the border around the icon. - /// Indicates if quiet zones around the QR code should be drawn. - /// Returns the QR code graphic as a bitmap. - public static Bitmap GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Bitmap? icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new QRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new QRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones); } } diff --git a/QRCoder/QRCodeData.cs b/QRCoder/QRCodeData.cs index 133221fa..0c27ff55 100644 --- a/QRCoder/QRCodeData.cs +++ b/QRCoder/QRCodeData.cs @@ -4,244 +4,227 @@ using System.IO; using System.IO.Compression; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents the data structure of a QR code. +/// +public class QRCodeData : IDisposable { /// - /// Represents the data structure of a QR code. + /// Gets or sets the module matrix of the QR code. + /// + public List ModuleMatrix { get; set; } + + /// + /// Initializes a new instance of the class with the specified version. /// - public class QRCodeData : IDisposable + /// The version of the QR code. + public QRCodeData(int version) { - /// - /// Gets or sets the module matrix of the QR code. - /// - public List ModuleMatrix { get; set; } + Version = version; + var size = ModulesPerSideFromVersion(version); + ModuleMatrix = new List(size); + for (var i = 0; i < size; i++) + ModuleMatrix.Add(new BitArray(size)); + } - /// - /// Initializes a new instance of the class with the specified version. - /// - /// The version of the QR code. - public QRCodeData(int version) - { - this.Version = version; - var size = ModulesPerSideFromVersion(version); - this.ModuleMatrix = new List(size); - for (var i = 0; i < size; i++) - this.ModuleMatrix.Add(new BitArray(size)); - } + /// + /// Initializes a new instance of the class with the specified version and padding option. + /// + /// The version of the QR code. + /// Indicates whether padding should be added to the QR code. + public QRCodeData(int version, bool addPadding) + { + Version = version; + var size = ModulesPerSideFromVersion(version) + (addPadding ? 8 : 0); + ModuleMatrix = new List(size); + for (var i = 0; i < size; i++) + ModuleMatrix.Add(new BitArray(size)); + } - /// - /// Initializes a new instance of the class with the specified version and padding option. - /// - /// The version of the QR code. - /// Indicates whether padding should be added to the QR code. - public QRCodeData(int version, bool addPadding) - { - this.Version = version; - var size = ModulesPerSideFromVersion(version) + (addPadding ? 8 : 0); - this.ModuleMatrix = new List(size); - for (var i = 0; i < size; i++) - this.ModuleMatrix.Add(new BitArray(size)); - } + /// + /// Initializes a new instance of the class with raw data from a specified path and compression mode. + /// + /// The path to the raw data file. + /// The compression mode used for the raw data. + public QRCodeData(string pathToRawData, Compression compressMode) : this(File.ReadAllBytes(pathToRawData), compressMode) + { + } - /// - /// Initializes a new instance of the class with raw data from a specified path and compression mode. - /// - /// The path to the raw data file. - /// The compression mode used for the raw data. - public QRCodeData(string pathToRawData, Compression compressMode) : this(File.ReadAllBytes(pathToRawData), compressMode) - { - } + /// + /// Initializes a new instance of the class with raw data and compression mode. + /// + /// The raw data of the QR code. + /// The compression mode used for the raw data. + public QRCodeData(byte[] rawData, Compression compressMode) + { + var bytes = new List(rawData); - /// - /// Initializes a new instance of the class with raw data and compression mode. - /// - /// The raw data of the QR code. - /// The compression mode used for the raw data. - public QRCodeData(byte[] rawData, Compression compressMode) + //Decompress + if (compressMode == Compression.Deflate) { - var bytes = new List(rawData); - - //Decompress - if (compressMode == Compression.Deflate) + using var input = new MemoryStream(bytes.ToArray()); + using var output = new MemoryStream(); + using (var dstream = new DeflateStream(input, CompressionMode.Decompress)) { - using (var input = new MemoryStream(bytes.ToArray())) - { - using (var output = new MemoryStream()) - { - using (var dstream = new DeflateStream(input, CompressionMode.Decompress)) - { - dstream.CopyTo(output); - } - bytes = new List(output.ToArray()); - } - } + dstream.CopyTo(output); } - else if (compressMode == Compression.GZip) + bytes = new List(output.ToArray()); + } + else if (compressMode == Compression.GZip) + { + using var input = new MemoryStream(bytes.ToArray()); + using var output = new MemoryStream(); + using (var dstream = new GZipStream(input, CompressionMode.Decompress)) { - using (var input = new MemoryStream(bytes.ToArray())) - { - using (var output = new MemoryStream()) - { - using (var dstream = new GZipStream(input, CompressionMode.Decompress)) - { - dstream.CopyTo(output); - } - bytes = new List(output.ToArray()); - } - } + dstream.CopyTo(output); } + bytes = new List(output.ToArray()); + } - if (bytes[0] != 0x51 || bytes[1] != 0x52 || bytes[2] != 0x52) - throw new Exception("Invalid raw data file. Filetype doesn't match \"QRR\"."); + if (bytes[0] != 0x51 || bytes[1] != 0x52 || bytes[2] != 0x52) + throw new Exception("Invalid raw data file. Filetype doesn't match \"QRR\"."); - //Set QR code version - var sideLen = (int)bytes[4]; - bytes.RemoveRange(0, 5); - this.Version = (sideLen - 21 - 8) / 4 + 1; + //Set QR code version + var sideLen = (int)bytes[4]; + bytes.RemoveRange(0, 5); + Version = (sideLen - 21 - 8) / 4 + 1; - //Unpack - var modules = new Queue(8 * bytes.Count); - foreach (var b in bytes) + //Unpack + var modules = new Queue(8 * bytes.Count); + foreach (var b in bytes) + { + var bArr = new BitArray(new byte[] { b }); + for (int i = 7; i >= 0; i--) { - var bArr = new BitArray(new byte[] { b }); - for (int i = 7; i >= 0; i--) - { - modules.Enqueue((b & (1 << i)) != 0); - } + modules.Enqueue((b & (1 << i)) != 0); } + } - //Build module matrix - this.ModuleMatrix = new List(sideLen); - for (int y = 0; y < sideLen; y++) + //Build module matrix + ModuleMatrix = new List(sideLen); + for (int y = 0; y < sideLen; y++) + { + ModuleMatrix.Add(new BitArray(sideLen)); + for (int x = 0; x < sideLen; x++) { - this.ModuleMatrix.Add(new BitArray(sideLen)); - for (int x = 0; x < sideLen; x++) - { - this.ModuleMatrix[y][x] = modules.Dequeue(); - } + ModuleMatrix[y][x] = modules.Dequeue(); } - } - /// - /// Gets the raw data of the QR code with the specified compression mode. - /// - /// The compression mode used for the raw data. - /// Returns the raw data of the QR code as a byte array. - public byte[] GetRawData(Compression compressMode) - { - var bytes = new List(); + } - //Add header - signature ("QRR") - bytes.AddRange(new byte[]{ 0x51, 0x52, 0x52, 0x00 }); + /// + /// Gets the raw data of the QR code with the specified compression mode. + /// + /// The compression mode used for the raw data. + /// Returns the raw data of the QR code as a byte array. + public byte[] GetRawData(Compression compressMode) + { + var bytes = new List(); - //Add header - rowsize - bytes.Add((byte)ModuleMatrix.Count); + //Add header - signature ("QRR") + bytes.AddRange(new byte[] { 0x51, 0x52, 0x52, 0x00 }); - //Build data queue - var dataQueue = new Queue(); - foreach (var row in ModuleMatrix) - { - foreach (var module in row) - { - dataQueue.Enqueue((bool)module ? 1 : 0); - } - } - for (int i = 0; i < 8 - (ModuleMatrix.Count * ModuleMatrix.Count) % 8; i++) + //Add header - rowsize + bytes.Add((byte)ModuleMatrix.Count); + + //Build data queue + var dataQueue = new Queue(); + foreach (var row in ModuleMatrix) + { + foreach (var module in row) { - dataQueue.Enqueue(0); + dataQueue.Enqueue((bool)module ? 1 : 0); } + } + for (int i = 0; i < 8 - (ModuleMatrix.Count * ModuleMatrix.Count) % 8; i++) + { + dataQueue.Enqueue(0); + } - //Process queue - while (dataQueue.Count > 0) + //Process queue + while (dataQueue.Count > 0) + { + byte b = 0; + for (int i = 7; i >= 0; i--) { - byte b = 0; - for (int i = 7; i >= 0; i--) - { - b += (byte)(dataQueue.Dequeue() << i); - } - bytes.Add(b); + b += (byte)(dataQueue.Dequeue() << i); } - var rawData = bytes.ToArray(); + bytes.Add(b); + } + var rawData = bytes.ToArray(); - //Compress stream (optional) - if (compressMode == Compression.Deflate) + //Compress stream (optional) + if (compressMode == Compression.Deflate) + { + using var output = new MemoryStream(); + using (var dstream = new DeflateStream(output, CompressionMode.Compress)) { - using (var output = new MemoryStream()) - { - using (var dstream = new DeflateStream(output, CompressionMode.Compress)) - { - dstream.Write(rawData, 0, rawData.Length); - } - rawData = output.ToArray(); - } + dstream.Write(rawData, 0, rawData.Length); } - else if (compressMode == Compression.GZip) + rawData = output.ToArray(); + } + else if (compressMode == Compression.GZip) + { + using var output = new MemoryStream(); + using (var gzipStream = new GZipStream(output, CompressionMode.Compress, true)) { - using (var output = new MemoryStream()) - { - using (GZipStream gzipStream = new GZipStream(output, CompressionMode.Compress, true)) - { - gzipStream.Write(rawData, 0, rawData.Length); - } - rawData = output.ToArray(); - } + gzipStream.Write(rawData, 0, rawData.Length); } - return rawData; + rawData = output.ToArray(); } + return rawData; + } - /// - /// Saves the raw data of the QR code to a specified file with the specified compression mode. - /// - /// The path to the file where the raw data will be saved. - /// The compression mode used for the raw data. - public void SaveRawData(string filePath, Compression compressMode) - { - File.WriteAllBytes(filePath, GetRawData(compressMode)); - } + /// + /// Saves the raw data of the QR code to a specified file with the specified compression mode. + /// + /// The path to the file where the raw data will be saved. + /// The compression mode used for the raw data. + public void SaveRawData(string filePath, Compression compressMode) + => File.WriteAllBytes(filePath, GetRawData(compressMode)); - /// - /// Gets the version of the QR code. - /// - public int Version { get; private set; } + /// + /// Gets the version of the QR code. + /// + public int Version { get; private set; } + + /// + /// Gets the number of modules per side from the specified version. + /// + /// The version of the QR code. + /// Returns the number of modules per side. + private static int ModulesPerSideFromVersion(int version) + => 21 + (version - 1) * 4; + /// + /// Releases all resources used by the . + /// + public void Dispose() + { + ModuleMatrix = null!; + Version = 0; + + } + + /// + /// Specifies the compression mode used for the raw data. + /// + public enum Compression + { /// - /// Gets the number of modules per side from the specified version. + /// No compression. /// - /// The version of the QR code. - /// Returns the number of modules per side. - private static int ModulesPerSideFromVersion(int version) - { - return 21 + (version - 1) * 4; - } - + Uncompressed, /// - /// Releases all resources used by the . + /// Deflate compression. /// - public void Dispose() - { - this.ModuleMatrix = null!; - this.Version = 0; - - } - + Deflate, /// - /// Specifies the compression mode used for the raw data. + /// GZip compression. /// - public enum Compression - { - /// - /// No compression. - /// - Uncompressed, - /// - /// Deflate compression. - /// - Deflate, - /// - /// GZip compression. - /// - GZip - } + GZip } } diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index a3215b38..f446a3f4 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -9,1394 +9,1365 @@ using System.Runtime.CompilerServices; using System.Text; -namespace QRCoder +namespace QRCoder; + +/// +/// Provides functionality to generate QR code data that can be used to create QR code images. +/// +public partial class QRCodeGenerator : IDisposable { + private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; + private static readonly int[] _capacityBaseValues = { 41, 25, 17, 10, 34, 20, 14, 8, 27, 16, 11, 7, 17, 10, 7, 4, 77, 47, 32, 20, 63, 38, 26, 16, 48, 29, 20, 12, 34, 20, 14, 8, 127, 77, 53, 32, 101, 61, 42, 26, 77, 47, 32, 20, 58, 35, 24, 15, 187, 114, 78, 48, 149, 90, 62, 38, 111, 67, 46, 28, 82, 50, 34, 21, 255, 154, 106, 65, 202, 122, 84, 52, 144, 87, 60, 37, 106, 64, 44, 27, 322, 195, 134, 82, 255, 154, 106, 65, 178, 108, 74, 45, 139, 84, 58, 36, 370, 224, 154, 95, 293, 178, 122, 75, 207, 125, 86, 53, 154, 93, 64, 39, 461, 279, 192, 118, 365, 221, 152, 93, 259, 157, 108, 66, 202, 122, 84, 52, 552, 335, 230, 141, 432, 262, 180, 111, 312, 189, 130, 80, 235, 143, 98, 60, 652, 395, 271, 167, 513, 311, 213, 131, 364, 221, 151, 93, 288, 174, 119, 74, 772, 468, 321, 198, 604, 366, 251, 155, 427, 259, 177, 109, 331, 200, 137, 85, 883, 535, 367, 226, 691, 419, 287, 177, 489, 296, 203, 125, 374, 227, 155, 96, 1022, 619, 425, 262, 796, 483, 331, 204, 580, 352, 241, 149, 427, 259, 177, 109, 1101, 667, 458, 282, 871, 528, 362, 223, 621, 376, 258, 159, 468, 283, 194, 120, 1250, 758, 520, 320, 991, 600, 412, 254, 703, 426, 292, 180, 530, 321, 220, 136, 1408, 854, 586, 361, 1082, 656, 450, 277, 775, 470, 322, 198, 602, 365, 250, 154, 1548, 938, 644, 397, 1212, 734, 504, 310, 876, 531, 364, 224, 674, 408, 280, 173, 1725, 1046, 718, 442, 1346, 816, 560, 345, 948, 574, 394, 243, 746, 452, 310, 191, 1903, 1153, 792, 488, 1500, 909, 624, 384, 1063, 644, 442, 272, 813, 493, 338, 208, 2061, 1249, 858, 528, 1600, 970, 666, 410, 1159, 702, 482, 297, 919, 557, 382, 235, 2232, 1352, 929, 572, 1708, 1035, 711, 438, 1224, 742, 509, 314, 969, 587, 403, 248, 2409, 1460, 1003, 618, 1872, 1134, 779, 480, 1358, 823, 565, 348, 1056, 640, 439, 270, 2620, 1588, 1091, 672, 2059, 1248, 857, 528, 1468, 890, 611, 376, 1108, 672, 461, 284, 2812, 1704, 1171, 721, 2188, 1326, 911, 561, 1588, 963, 661, 407, 1228, 744, 511, 315, 3057, 1853, 1273, 784, 2395, 1451, 997, 614, 1718, 1041, 715, 440, 1286, 779, 535, 330, 3283, 1990, 1367, 842, 2544, 1542, 1059, 652, 1804, 1094, 751, 462, 1425, 864, 593, 365, 3517, 2132, 1465, 902, 2701, 1637, 1125, 692, 1933, 1172, 805, 496, 1501, 910, 625, 385, 3669, 2223, 1528, 940, 2857, 1732, 1190, 732, 2085, 1263, 868, 534, 1581, 958, 658, 405, 3909, 2369, 1628, 1002, 3035, 1839, 1264, 778, 2181, 1322, 908, 559, 1677, 1016, 698, 430, 4158, 2520, 1732, 1066, 3289, 1994, 1370, 843, 2358, 1429, 982, 604, 1782, 1080, 742, 457, 4417, 2677, 1840, 1132, 3486, 2113, 1452, 894, 2473, 1499, 1030, 634, 1897, 1150, 790, 486, 4686, 2840, 1952, 1201, 3693, 2238, 1538, 947, 2670, 1618, 1112, 684, 2022, 1226, 842, 518, 4965, 3009, 2068, 1273, 3909, 2369, 1628, 1002, 2805, 1700, 1168, 719, 2157, 1307, 898, 553, 5253, 3183, 2188, 1347, 4134, 2506, 1722, 1060, 2949, 1787, 1228, 756, 2301, 1394, 958, 590, 5529, 3351, 2303, 1417, 4343, 2632, 1809, 1113, 3081, 1867, 1283, 790, 2361, 1431, 983, 605, 5836, 3537, 2431, 1496, 4588, 2780, 1911, 1176, 3244, 1966, 1351, 832, 2524, 1530, 1051, 647, 6153, 3729, 2563, 1577, 4775, 2894, 1989, 1224, 3417, 2071, 1423, 876, 2625, 1591, 1093, 673, 6479, 3927, 2699, 1661, 5039, 3054, 2099, 1292, 3599, 2181, 1499, 923, 2735, 1658, 1139, 701, 6743, 4087, 2809, 1729, 5313, 3220, 2213, 1362, 3791, 2298, 1579, 972, 2927, 1774, 1219, 750, 7089, 4296, 2953, 1817, 5596, 3391, 2331, 1435, 3993, 2420, 1663, 1024, 3057, 1852, 1273, 784 }; + private static readonly int[] _capacityECCBaseValues = { 19, 7, 1, 19, 0, 0, 16, 10, 1, 16, 0, 0, 13, 13, 1, 13, 0, 0, 9, 17, 1, 9, 0, 0, 34, 10, 1, 34, 0, 0, 28, 16, 1, 28, 0, 0, 22, 22, 1, 22, 0, 0, 16, 28, 1, 16, 0, 0, 55, 15, 1, 55, 0, 0, 44, 26, 1, 44, 0, 0, 34, 18, 2, 17, 0, 0, 26, 22, 2, 13, 0, 0, 80, 20, 1, 80, 0, 0, 64, 18, 2, 32, 0, 0, 48, 26, 2, 24, 0, 0, 36, 16, 4, 9, 0, 0, 108, 26, 1, 108, 0, 0, 86, 24, 2, 43, 0, 0, 62, 18, 2, 15, 2, 16, 46, 22, 2, 11, 2, 12, 136, 18, 2, 68, 0, 0, 108, 16, 4, 27, 0, 0, 76, 24, 4, 19, 0, 0, 60, 28, 4, 15, 0, 0, 156, 20, 2, 78, 0, 0, 124, 18, 4, 31, 0, 0, 88, 18, 2, 14, 4, 15, 66, 26, 4, 13, 1, 14, 194, 24, 2, 97, 0, 0, 154, 22, 2, 38, 2, 39, 110, 22, 4, 18, 2, 19, 86, 26, 4, 14, 2, 15, 232, 30, 2, 116, 0, 0, 182, 22, 3, 36, 2, 37, 132, 20, 4, 16, 4, 17, 100, 24, 4, 12, 4, 13, 274, 18, 2, 68, 2, 69, 216, 26, 4, 43, 1, 44, 154, 24, 6, 19, 2, 20, 122, 28, 6, 15, 2, 16, 324, 20, 4, 81, 0, 0, 254, 30, 1, 50, 4, 51, 180, 28, 4, 22, 4, 23, 140, 24, 3, 12, 8, 13, 370, 24, 2, 92, 2, 93, 290, 22, 6, 36, 2, 37, 206, 26, 4, 20, 6, 21, 158, 28, 7, 14, 4, 15, 428, 26, 4, 107, 0, 0, 334, 22, 8, 37, 1, 38, 244, 24, 8, 20, 4, 21, 180, 22, 12, 11, 4, 12, 461, 30, 3, 115, 1, 116, 365, 24, 4, 40, 5, 41, 261, 20, 11, 16, 5, 17, 197, 24, 11, 12, 5, 13, 523, 22, 5, 87, 1, 88, 415, 24, 5, 41, 5, 42, 295, 30, 5, 24, 7, 25, 223, 24, 11, 12, 7, 13, 589, 24, 5, 98, 1, 99, 453, 28, 7, 45, 3, 46, 325, 24, 15, 19, 2, 20, 253, 30, 3, 15, 13, 16, 647, 28, 1, 107, 5, 108, 507, 28, 10, 46, 1, 47, 367, 28, 1, 22, 15, 23, 283, 28, 2, 14, 17, 15, 721, 30, 5, 120, 1, 121, 563, 26, 9, 43, 4, 44, 397, 28, 17, 22, 1, 23, 313, 28, 2, 14, 19, 15, 795, 28, 3, 113, 4, 114, 627, 26, 3, 44, 11, 45, 445, 26, 17, 21, 4, 22, 341, 26, 9, 13, 16, 14, 861, 28, 3, 107, 5, 108, 669, 26, 3, 41, 13, 42, 485, 30, 15, 24, 5, 25, 385, 28, 15, 15, 10, 16, 932, 28, 4, 116, 4, 117, 714, 26, 17, 42, 0, 0, 512, 28, 17, 22, 6, 23, 406, 30, 19, 16, 6, 17, 1006, 28, 2, 111, 7, 112, 782, 28, 17, 46, 0, 0, 568, 30, 7, 24, 16, 25, 442, 24, 34, 13, 0, 0, 1094, 30, 4, 121, 5, 122, 860, 28, 4, 47, 14, 48, 614, 30, 11, 24, 14, 25, 464, 30, 16, 15, 14, 16, 1174, 30, 6, 117, 4, 118, 914, 28, 6, 45, 14, 46, 664, 30, 11, 24, 16, 25, 514, 30, 30, 16, 2, 17, 1276, 26, 8, 106, 4, 107, 1000, 28, 8, 47, 13, 48, 718, 30, 7, 24, 22, 25, 538, 30, 22, 15, 13, 16, 1370, 28, 10, 114, 2, 115, 1062, 28, 19, 46, 4, 47, 754, 28, 28, 22, 6, 23, 596, 30, 33, 16, 4, 17, 1468, 30, 8, 122, 4, 123, 1128, 28, 22, 45, 3, 46, 808, 30, 8, 23, 26, 24, 628, 30, 12, 15, 28, 16, 1531, 30, 3, 117, 10, 118, 1193, 28, 3, 45, 23, 46, 871, 30, 4, 24, 31, 25, 661, 30, 11, 15, 31, 16, 1631, 30, 7, 116, 7, 117, 1267, 28, 21, 45, 7, 46, 911, 30, 1, 23, 37, 24, 701, 30, 19, 15, 26, 16, 1735, 30, 5, 115, 10, 116, 1373, 28, 19, 47, 10, 48, 985, 30, 15, 24, 25, 25, 745, 30, 23, 15, 25, 16, 1843, 30, 13, 115, 3, 116, 1455, 28, 2, 46, 29, 47, 1033, 30, 42, 24, 1, 25, 793, 30, 23, 15, 28, 16, 1955, 30, 17, 115, 0, 0, 1541, 28, 10, 46, 23, 47, 1115, 30, 10, 24, 35, 25, 845, 30, 19, 15, 35, 16, 2071, 30, 17, 115, 1, 116, 1631, 28, 14, 46, 21, 47, 1171, 30, 29, 24, 19, 25, 901, 30, 11, 15, 46, 16, 2191, 30, 13, 115, 6, 116, 1725, 28, 14, 46, 23, 47, 1231, 30, 44, 24, 7, 25, 961, 30, 59, 16, 1, 17, 2306, 30, 12, 121, 7, 122, 1812, 28, 12, 47, 26, 48, 1286, 30, 39, 24, 14, 25, 986, 30, 22, 15, 41, 16, 2434, 30, 6, 121, 14, 122, 1914, 28, 6, 47, 34, 48, 1354, 30, 46, 24, 10, 25, 1054, 30, 2, 15, 64, 16, 2566, 30, 17, 122, 4, 123, 1992, 28, 29, 46, 14, 47, 1426, 30, 49, 24, 10, 25, 1096, 30, 24, 15, 46, 16, 2702, 30, 4, 122, 18, 123, 2102, 28, 13, 46, 32, 47, 1502, 30, 48, 24, 14, 25, 1142, 30, 42, 15, 32, 16, 2812, 30, 20, 117, 4, 118, 2216, 28, 40, 47, 7, 48, 1582, 30, 43, 24, 22, 25, 1222, 30, 10, 15, 67, 16, 2956, 30, 19, 118, 6, 119, 2334, 28, 18, 47, 31, 48, 1666, 30, 34, 24, 34, 25, 1276, 30, 20, 15, 61, 16 }; + private static readonly int[] _alignmentPatternBaseValues = { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; + private static readonly int[] _remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; + + private static readonly Dictionary _alignmentPatternTable = CreateAlignmentPatternTable(); + private static readonly List _capacityECCTable = CreateCapacityECCTable(); + private static readonly List _capacityTable = CreateCapacityTable(); + private static readonly int[] _galoisFieldByExponentAlpha = { 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 }; + private static readonly int[] _galoisFieldByIntegerValue = { 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 }; + private static readonly Dictionary _alphanumEncDict = CreateAlphanumEncDict(); + /// - /// Provides functionality to generate QR code data that can be used to create QR code images. + /// Initializes the QR code generator /// - public partial class QRCodeGenerator : IDisposable + public QRCodeGenerator() { - private static readonly char[] alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; - private static readonly int[] capacityBaseValues = { 41, 25, 17, 10, 34, 20, 14, 8, 27, 16, 11, 7, 17, 10, 7, 4, 77, 47, 32, 20, 63, 38, 26, 16, 48, 29, 20, 12, 34, 20, 14, 8, 127, 77, 53, 32, 101, 61, 42, 26, 77, 47, 32, 20, 58, 35, 24, 15, 187, 114, 78, 48, 149, 90, 62, 38, 111, 67, 46, 28, 82, 50, 34, 21, 255, 154, 106, 65, 202, 122, 84, 52, 144, 87, 60, 37, 106, 64, 44, 27, 322, 195, 134, 82, 255, 154, 106, 65, 178, 108, 74, 45, 139, 84, 58, 36, 370, 224, 154, 95, 293, 178, 122, 75, 207, 125, 86, 53, 154, 93, 64, 39, 461, 279, 192, 118, 365, 221, 152, 93, 259, 157, 108, 66, 202, 122, 84, 52, 552, 335, 230, 141, 432, 262, 180, 111, 312, 189, 130, 80, 235, 143, 98, 60, 652, 395, 271, 167, 513, 311, 213, 131, 364, 221, 151, 93, 288, 174, 119, 74, 772, 468, 321, 198, 604, 366, 251, 155, 427, 259, 177, 109, 331, 200, 137, 85, 883, 535, 367, 226, 691, 419, 287, 177, 489, 296, 203, 125, 374, 227, 155, 96, 1022, 619, 425, 262, 796, 483, 331, 204, 580, 352, 241, 149, 427, 259, 177, 109, 1101, 667, 458, 282, 871, 528, 362, 223, 621, 376, 258, 159, 468, 283, 194, 120, 1250, 758, 520, 320, 991, 600, 412, 254, 703, 426, 292, 180, 530, 321, 220, 136, 1408, 854, 586, 361, 1082, 656, 450, 277, 775, 470, 322, 198, 602, 365, 250, 154, 1548, 938, 644, 397, 1212, 734, 504, 310, 876, 531, 364, 224, 674, 408, 280, 173, 1725, 1046, 718, 442, 1346, 816, 560, 345, 948, 574, 394, 243, 746, 452, 310, 191, 1903, 1153, 792, 488, 1500, 909, 624, 384, 1063, 644, 442, 272, 813, 493, 338, 208, 2061, 1249, 858, 528, 1600, 970, 666, 410, 1159, 702, 482, 297, 919, 557, 382, 235, 2232, 1352, 929, 572, 1708, 1035, 711, 438, 1224, 742, 509, 314, 969, 587, 403, 248, 2409, 1460, 1003, 618, 1872, 1134, 779, 480, 1358, 823, 565, 348, 1056, 640, 439, 270, 2620, 1588, 1091, 672, 2059, 1248, 857, 528, 1468, 890, 611, 376, 1108, 672, 461, 284, 2812, 1704, 1171, 721, 2188, 1326, 911, 561, 1588, 963, 661, 407, 1228, 744, 511, 315, 3057, 1853, 1273, 784, 2395, 1451, 997, 614, 1718, 1041, 715, 440, 1286, 779, 535, 330, 3283, 1990, 1367, 842, 2544, 1542, 1059, 652, 1804, 1094, 751, 462, 1425, 864, 593, 365, 3517, 2132, 1465, 902, 2701, 1637, 1125, 692, 1933, 1172, 805, 496, 1501, 910, 625, 385, 3669, 2223, 1528, 940, 2857, 1732, 1190, 732, 2085, 1263, 868, 534, 1581, 958, 658, 405, 3909, 2369, 1628, 1002, 3035, 1839, 1264, 778, 2181, 1322, 908, 559, 1677, 1016, 698, 430, 4158, 2520, 1732, 1066, 3289, 1994, 1370, 843, 2358, 1429, 982, 604, 1782, 1080, 742, 457, 4417, 2677, 1840, 1132, 3486, 2113, 1452, 894, 2473, 1499, 1030, 634, 1897, 1150, 790, 486, 4686, 2840, 1952, 1201, 3693, 2238, 1538, 947, 2670, 1618, 1112, 684, 2022, 1226, 842, 518, 4965, 3009, 2068, 1273, 3909, 2369, 1628, 1002, 2805, 1700, 1168, 719, 2157, 1307, 898, 553, 5253, 3183, 2188, 1347, 4134, 2506, 1722, 1060, 2949, 1787, 1228, 756, 2301, 1394, 958, 590, 5529, 3351, 2303, 1417, 4343, 2632, 1809, 1113, 3081, 1867, 1283, 790, 2361, 1431, 983, 605, 5836, 3537, 2431, 1496, 4588, 2780, 1911, 1176, 3244, 1966, 1351, 832, 2524, 1530, 1051, 647, 6153, 3729, 2563, 1577, 4775, 2894, 1989, 1224, 3417, 2071, 1423, 876, 2625, 1591, 1093, 673, 6479, 3927, 2699, 1661, 5039, 3054, 2099, 1292, 3599, 2181, 1499, 923, 2735, 1658, 1139, 701, 6743, 4087, 2809, 1729, 5313, 3220, 2213, 1362, 3791, 2298, 1579, 972, 2927, 1774, 1219, 750, 7089, 4296, 2953, 1817, 5596, 3391, 2331, 1435, 3993, 2420, 1663, 1024, 3057, 1852, 1273, 784 }; - private static readonly int[] capacityECCBaseValues = { 19, 7, 1, 19, 0, 0, 16, 10, 1, 16, 0, 0, 13, 13, 1, 13, 0, 0, 9, 17, 1, 9, 0, 0, 34, 10, 1, 34, 0, 0, 28, 16, 1, 28, 0, 0, 22, 22, 1, 22, 0, 0, 16, 28, 1, 16, 0, 0, 55, 15, 1, 55, 0, 0, 44, 26, 1, 44, 0, 0, 34, 18, 2, 17, 0, 0, 26, 22, 2, 13, 0, 0, 80, 20, 1, 80, 0, 0, 64, 18, 2, 32, 0, 0, 48, 26, 2, 24, 0, 0, 36, 16, 4, 9, 0, 0, 108, 26, 1, 108, 0, 0, 86, 24, 2, 43, 0, 0, 62, 18, 2, 15, 2, 16, 46, 22, 2, 11, 2, 12, 136, 18, 2, 68, 0, 0, 108, 16, 4, 27, 0, 0, 76, 24, 4, 19, 0, 0, 60, 28, 4, 15, 0, 0, 156, 20, 2, 78, 0, 0, 124, 18, 4, 31, 0, 0, 88, 18, 2, 14, 4, 15, 66, 26, 4, 13, 1, 14, 194, 24, 2, 97, 0, 0, 154, 22, 2, 38, 2, 39, 110, 22, 4, 18, 2, 19, 86, 26, 4, 14, 2, 15, 232, 30, 2, 116, 0, 0, 182, 22, 3, 36, 2, 37, 132, 20, 4, 16, 4, 17, 100, 24, 4, 12, 4, 13, 274, 18, 2, 68, 2, 69, 216, 26, 4, 43, 1, 44, 154, 24, 6, 19, 2, 20, 122, 28, 6, 15, 2, 16, 324, 20, 4, 81, 0, 0, 254, 30, 1, 50, 4, 51, 180, 28, 4, 22, 4, 23, 140, 24, 3, 12, 8, 13, 370, 24, 2, 92, 2, 93, 290, 22, 6, 36, 2, 37, 206, 26, 4, 20, 6, 21, 158, 28, 7, 14, 4, 15, 428, 26, 4, 107, 0, 0, 334, 22, 8, 37, 1, 38, 244, 24, 8, 20, 4, 21, 180, 22, 12, 11, 4, 12, 461, 30, 3, 115, 1, 116, 365, 24, 4, 40, 5, 41, 261, 20, 11, 16, 5, 17, 197, 24, 11, 12, 5, 13, 523, 22, 5, 87, 1, 88, 415, 24, 5, 41, 5, 42, 295, 30, 5, 24, 7, 25, 223, 24, 11, 12, 7, 13, 589, 24, 5, 98, 1, 99, 453, 28, 7, 45, 3, 46, 325, 24, 15, 19, 2, 20, 253, 30, 3, 15, 13, 16, 647, 28, 1, 107, 5, 108, 507, 28, 10, 46, 1, 47, 367, 28, 1, 22, 15, 23, 283, 28, 2, 14, 17, 15, 721, 30, 5, 120, 1, 121, 563, 26, 9, 43, 4, 44, 397, 28, 17, 22, 1, 23, 313, 28, 2, 14, 19, 15, 795, 28, 3, 113, 4, 114, 627, 26, 3, 44, 11, 45, 445, 26, 17, 21, 4, 22, 341, 26, 9, 13, 16, 14, 861, 28, 3, 107, 5, 108, 669, 26, 3, 41, 13, 42, 485, 30, 15, 24, 5, 25, 385, 28, 15, 15, 10, 16, 932, 28, 4, 116, 4, 117, 714, 26, 17, 42, 0, 0, 512, 28, 17, 22, 6, 23, 406, 30, 19, 16, 6, 17, 1006, 28, 2, 111, 7, 112, 782, 28, 17, 46, 0, 0, 568, 30, 7, 24, 16, 25, 442, 24, 34, 13, 0, 0, 1094, 30, 4, 121, 5, 122, 860, 28, 4, 47, 14, 48, 614, 30, 11, 24, 14, 25, 464, 30, 16, 15, 14, 16, 1174, 30, 6, 117, 4, 118, 914, 28, 6, 45, 14, 46, 664, 30, 11, 24, 16, 25, 514, 30, 30, 16, 2, 17, 1276, 26, 8, 106, 4, 107, 1000, 28, 8, 47, 13, 48, 718, 30, 7, 24, 22, 25, 538, 30, 22, 15, 13, 16, 1370, 28, 10, 114, 2, 115, 1062, 28, 19, 46, 4, 47, 754, 28, 28, 22, 6, 23, 596, 30, 33, 16, 4, 17, 1468, 30, 8, 122, 4, 123, 1128, 28, 22, 45, 3, 46, 808, 30, 8, 23, 26, 24, 628, 30, 12, 15, 28, 16, 1531, 30, 3, 117, 10, 118, 1193, 28, 3, 45, 23, 46, 871, 30, 4, 24, 31, 25, 661, 30, 11, 15, 31, 16, 1631, 30, 7, 116, 7, 117, 1267, 28, 21, 45, 7, 46, 911, 30, 1, 23, 37, 24, 701, 30, 19, 15, 26, 16, 1735, 30, 5, 115, 10, 116, 1373, 28, 19, 47, 10, 48, 985, 30, 15, 24, 25, 25, 745, 30, 23, 15, 25, 16, 1843, 30, 13, 115, 3, 116, 1455, 28, 2, 46, 29, 47, 1033, 30, 42, 24, 1, 25, 793, 30, 23, 15, 28, 16, 1955, 30, 17, 115, 0, 0, 1541, 28, 10, 46, 23, 47, 1115, 30, 10, 24, 35, 25, 845, 30, 19, 15, 35, 16, 2071, 30, 17, 115, 1, 116, 1631, 28, 14, 46, 21, 47, 1171, 30, 29, 24, 19, 25, 901, 30, 11, 15, 46, 16, 2191, 30, 13, 115, 6, 116, 1725, 28, 14, 46, 23, 47, 1231, 30, 44, 24, 7, 25, 961, 30, 59, 16, 1, 17, 2306, 30, 12, 121, 7, 122, 1812, 28, 12, 47, 26, 48, 1286, 30, 39, 24, 14, 25, 986, 30, 22, 15, 41, 16, 2434, 30, 6, 121, 14, 122, 1914, 28, 6, 47, 34, 48, 1354, 30, 46, 24, 10, 25, 1054, 30, 2, 15, 64, 16, 2566, 30, 17, 122, 4, 123, 1992, 28, 29, 46, 14, 47, 1426, 30, 49, 24, 10, 25, 1096, 30, 24, 15, 46, 16, 2702, 30, 4, 122, 18, 123, 2102, 28, 13, 46, 32, 47, 1502, 30, 48, 24, 14, 25, 1142, 30, 42, 15, 32, 16, 2812, 30, 20, 117, 4, 118, 2216, 28, 40, 47, 7, 48, 1582, 30, 43, 24, 22, 25, 1222, 30, 10, 15, 67, 16, 2956, 30, 19, 118, 6, 119, 2334, 28, 18, 47, 31, 48, 1666, 30, 34, 24, 34, 25, 1276, 30, 20, 15, 61, 16 }; - private static readonly int[] alignmentPatternBaseValues = { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; - private static readonly int[] remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; - - private static readonly Dictionary alignmentPatternTable = CreateAlignmentPatternTable(); - private static readonly List capacityECCTable = CreateCapacityECCTable(); - private static readonly List capacityTable = CreateCapacityTable(); - private static readonly int[] galoisFieldByExponentAlpha = { 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 }; - private static readonly int[] galoisFieldByIntegerValue = { 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 }; - private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); - - /// - /// Initializes the QR code generator - /// - public QRCodeGenerator() - { - } + } - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public QRCodeData CreateQrCode(PayloadGenerator.Payload payload) - { - return GenerateQrCode(payload); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A payload object, generated by the PayloadGenerator-class + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public QRCodeData CreateQrCode(PayloadGenerator.Payload payload) + => GenerateQrCode(payload); - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public QRCodeData CreateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) - { - return GenerateQrCode(payload, eccLevel); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A payload object, generated by the PayloadGenerator-class + /// The level of error correction data + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public QRCodeData CreateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) + => GenerateQrCode(payload, eccLevel); - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// The payload which shall be encoded in the QR code - /// The level of error correction data - /// Shall the generator be forced to work in UTF-8 mode? - /// Should the byte-order-mark be used? - /// Which ECI mode shall be used? - /// Set fixed QR code target version. - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public QRCodeData CreateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1) - { - return GenerateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// The payload which shall be encoded in the QR code + /// The level of error correction data + /// Shall the generator be forced to work in UTF-8 mode? + /// Should the byte-order-mark be used? + /// Which ECI mode shall be used? + /// Set fixed QR code target version. + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public QRCodeData CreateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1) + => GenerateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A byte array which shall be encoded/stored in the QR code - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public QRCodeData CreateQrCode(byte[] binaryData, ECCLevel eccLevel) - { - return GenerateQrCode(binaryData, eccLevel); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A byte array which shall be encoded/stored in the QR code + /// The level of error correction data + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public QRCodeData CreateQrCode(byte[] binaryData, ECCLevel eccLevel) + => GenerateQrCode(binaryData, eccLevel); - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload) - { - return GenerateQrCode(payload.ToString(), payload.EccLevel, false, false, payload.EciMode, payload.Version); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A payload object, generated by the PayloadGenerator-class + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload) + => GenerateQrCode(payload.ToString(), payload.EccLevel, false, false, payload.EciMode, payload.Version); - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A payload object, generated by the PayloadGenerator-class - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A payload object, generated by the PayloadGenerator-class + /// The level of error correction data + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public static QRCodeData GenerateQrCode(PayloadGenerator.Payload payload, ECCLevel eccLevel) + { + if (eccLevel == ECCLevel.Default) + eccLevel = payload.EccLevel; + else if (payload.EccLevel != ECCLevel.Default && eccLevel != payload.EccLevel) + throw new ArgumentOutOfRangeException(nameof(eccLevel), $"The provided payload requires a ECC level of {eccLevel}."); + return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version); + } + + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// The payload which shall be encoded in the QR code + /// The level of error correction data + /// Shall the generator be forced to work in UTF-8 mode? + /// Should the byte-order-mark be used? + /// Which ECI mode shall be used? + /// Set fixed QR code target version. + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1) + { + eccLevel = ValidateECCLevel(eccLevel); + var encoding = GetEncodingFromPlaintext(plainText, forceUtf8); + var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8); + var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8); + int version = requestedVersion; + int minVersion = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); + if (version == -1) { - if (eccLevel == ECCLevel.Default) - eccLevel = payload.EccLevel; - else if (payload.EccLevel != ECCLevel.Default && eccLevel != payload.EccLevel) - throw new ArgumentOutOfRangeException(nameof(eccLevel), $"The provided payload requires a ECC level of {eccLevel}."); - return GenerateQrCode(payload.ToString(), eccLevel, false, false, payload.EciMode, payload.Version); + version = minVersion; } - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// The payload which shall be encoded in the QR code - /// The level of error correction data - /// Shall the generator be forced to work in UTF-8 mode? - /// Should the byte-order-mark be used? - /// Which ECI mode shall be used? - /// Set fixed QR code target version. - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1) + else { - eccLevel = ValidateECCLevel(eccLevel); - EncodingMode encoding = GetEncodingFromPlaintext(plainText, forceUtf8); - var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8); - var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8); - int version = requestedVersion; - int minVersion = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); - if (version == -1) + //Version was passed as fixed version via parameter. Thus let's check if chosen version is valid. + if (minVersion > version) { - version = minVersion; + var maxSizeByte = _capacityTable[version - 1].Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; + throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte); } - else - { - //Version was passed as fixed version via parameter. Thus let's check if chosen version is valid. - if (minVersion > version) - { - var maxSizeByte = capacityTable[version - 1].Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; - throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte); - } - } - - var modeIndicatorLength = eciMode != EciMode.Default ? 16 : 4; - var countIndicatorLength = GetCountIndicatorLength(version, encoding); - var completeBitArrayLength = modeIndicatorLength + countIndicatorLength + codedText.Length; + } - var completeBitArray = new BitArray(completeBitArrayLength); + var modeIndicatorLength = eciMode != EciMode.Default ? 16 : 4; + var countIndicatorLength = GetCountIndicatorLength(version, encoding); + var completeBitArrayLength = modeIndicatorLength + countIndicatorLength + codedText.Length; - // write mode indicator - var completeBitArrayIndex = 0; - if (eciMode != EciMode.Default) - { - completeBitArrayIndex = DecToBin((int)EncodingMode.ECI, 4, completeBitArray, completeBitArrayIndex); - completeBitArrayIndex = DecToBin((int)eciMode, 8, completeBitArray, completeBitArrayIndex); - } - completeBitArrayIndex = DecToBin((int)encoding, 4, completeBitArray, completeBitArrayIndex); - // write count indicator - completeBitArrayIndex = DecToBin(dataInputLength, countIndicatorLength, completeBitArray, completeBitArrayIndex); - // write data - for (int i = 0; i < codedText.Length; i++) - { - completeBitArray[completeBitArrayIndex++] = codedText[i]; - } + var completeBitArray = new BitArray(completeBitArrayLength); - return GenerateQrCode(completeBitArray, eccLevel, version); + // write mode indicator + var completeBitArrayIndex = 0; + if (eciMode != EciMode.Default) + { + completeBitArrayIndex = DecToBin((int)EncodingMode.ECI, 4, completeBitArray, completeBitArrayIndex); + completeBitArrayIndex = DecToBin((int)eciMode, 8, completeBitArray, completeBitArrayIndex); } - - /// - /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. - /// - /// A byte array which shall be encoded/stored in the QR code - /// The level of error correction data - /// Thrown when the payload is too big to be encoded in a QR code. - /// Returns the raw QR code data which can be used for rendering. - public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) + completeBitArrayIndex = DecToBin((int)encoding, 4, completeBitArray, completeBitArrayIndex); + // write count indicator + completeBitArrayIndex = DecToBin(dataInputLength, countIndicatorLength, completeBitArray, completeBitArrayIndex); + // write data + for (int i = 0; i < codedText.Length; i++) { - eccLevel = ValidateECCLevel(eccLevel); - int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); + completeBitArray[completeBitArrayIndex++] = codedText[i]; + } - int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); - // Convert byte array to bit array, with prefix padding for mode indicator and count indicator - var bitArray = ToBitArray(binaryData, prefixZeros: 4 + countIndicatorLen); - // Add mode indicator and count indicator - var index = DecToBin((int)EncodingMode.Byte, 4, bitArray, 0); - DecToBin(binaryData.Length, countIndicatorLen, bitArray, index); + return GenerateQrCode(completeBitArray, eccLevel, version); + } - return GenerateQrCode(bitArray, eccLevel, version); - } + /// + /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. + /// + /// A byte array which shall be encoded/stored in the QR code + /// The level of error correction data + /// Thrown when the payload is too big to be encoded in a QR code. + /// Returns the raw QR code data which can be used for rendering. + public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) + { + eccLevel = ValidateECCLevel(eccLevel); + int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); - /// - /// Validates the specified error correction level. - /// Returns the provided level if it is valid, or the level M if the provided level is Default. - /// Throws an exception if an invalid level is provided. - /// - private static ECCLevel ValidateECCLevel(ECCLevel eccLevel) - { - switch (eccLevel) - { - case ECCLevel.L: - case ECCLevel.M: - case ECCLevel.Q: - case ECCLevel.H: - return eccLevel; - case ECCLevel.Default: - return ECCLevel.M; - default: - throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Invalid error correction level."); - } - } + int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); + // Convert byte array to bit array, with prefix padding for mode indicator and count indicator + var bitArray = ToBitArray(binaryData, prefixZeros: 4 + countIndicatorLen); + // Add mode indicator and count indicator + var index = DecToBin((int)EncodingMode.Byte, 4, bitArray, 0); + DecToBin(binaryData.Length, countIndicatorLen, bitArray, index); + + return GenerateQrCode(bitArray, eccLevel, version); + } - private static readonly BitArray _repeatingPattern = new BitArray( - new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true }); - - /// - /// Generates a QR code data structure using the provided BitArray, error correction level, and version. - /// The BitArray provided is assumed to already include the count, encoding mode, and/or ECI mode information. - /// - /// The BitArray containing the binary-encoded data to be included in the QR code. It should already contain the count, encoding mode, and/or ECI mode information. - /// The desired error correction level for the QR code. This impacts how much data can be recovered if damaged. - /// The version of the QR code, determining the size and complexity of the QR code data matrix. - /// A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis. - private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, int version) + /// + /// Validates the specified error correction level. + /// Returns the provided level if it is valid, or the level M if the provided level is Default. + /// Throws an exception if an invalid level is provided. + /// + private static ECCLevel ValidateECCLevel(ECCLevel eccLevel) + { + return eccLevel switch { - var eccInfo = capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); + ECCLevel.L or ECCLevel.M or ECCLevel.Q or ECCLevel.H => eccLevel, + ECCLevel.Default => ECCLevel.M, + _ => throw new ArgumentOutOfRangeException(nameof(eccLevel), eccLevel, "Invalid error correction level."), + }; + } - // Fill up data code word - PadData(); + private static readonly BitArray _repeatingPattern = new BitArray( + new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true }); - // Calculate error correction blocks - var codeWordWithECC = CalculateECCBlocks(); + /// + /// Generates a QR code data structure using the provided BitArray, error correction level, and version. + /// The BitArray provided is assumed to already include the count, encoding mode, and/or ECI mode information. + /// + /// The BitArray containing the binary-encoded data to be included in the QR code. It should already contain the count, encoding mode, and/or ECI mode information. + /// The desired error correction level for the QR code. This impacts how much data can be recovered if damaged. + /// The version of the QR code, determining the size and complexity of the QR code data matrix. + /// A QRCodeData structure containing the full QR code matrix, which can be used for rendering or analysis. + private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, int version) + { + var eccInfo = _capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); - // Calculate interleaved code word lengths - var interleavedLength = CalculateInterleavedLength(); + // Fill up data code word + PadData(); - // Interleave code words - var interleavedData = InterleaveData(); + // Calculate error correction blocks + var codeWordWithECC = CalculateECCBlocks(); - // Place interleaved data on module matrix - var qrData = PlaceModules(); + // Calculate interleaved code word lengths + var interleavedLength = CalculateInterleavedLength(); - return qrData; + // Interleave code words + var interleavedData = InterleaveData(); + // Place interleaved data on module matrix + var qrData = PlaceModules(); - // fills the bit array with a repeating pattern to reach the required length - void PadData() + return qrData; + + + // fills the bit array with a repeating pattern to reach the required length + void PadData() + { + var dataLength = eccInfo.TotalDataCodewords * 8; + var lengthDiff = dataLength - bitArray.Length; + if (lengthDiff > 0) { - var dataLength = eccInfo.TotalDataCodewords * 8; - var lengthDiff = dataLength - bitArray.Length; - if (lengthDiff > 0) + // set 'write index' to end of existing bit array + var index = bitArray.Length; + // extend bit array to required length + bitArray.Length = dataLength; + // pad with 4 zeros (or less if lengthDiff < 4) + index += Math.Min(lengthDiff, 4); + // pad to nearest 8 bit boundary + if ((uint)index % 8 != 0) + index += 8 - (int)((uint)index % 8); + // pad with repeating pattern + var repeatingPatternIndex = 0; + while (index < dataLength) { - // set 'write index' to end of existing bit array - var index = bitArray.Length; - // extend bit array to required length - bitArray.Length = dataLength; - // pad with 4 zeros (or less if lengthDiff < 4) - index += Math.Min(lengthDiff, 4); - // pad to nearest 8 bit boundary - if ((uint)index % 8 != 0) - index += 8 - (int)((uint)index % 8); - // pad with repeating pattern - var repeatingPatternIndex = 0; - while (index < dataLength) - { - bitArray[index++] = _repeatingPattern[repeatingPatternIndex++]; - if (repeatingPatternIndex >= _repeatingPattern.Length) - repeatingPatternIndex = 0; - } + bitArray[index++] = _repeatingPattern[repeatingPatternIndex++]; + if (repeatingPatternIndex >= _repeatingPattern.Length) + repeatingPatternIndex = 0; } } + } - List CalculateECCBlocks() + List CalculateECCBlocks() + { + List codewordBlocks; + // Generate the generator polynomial using the number of ECC words. + using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) { - List codewordBlocks; - // Generate the generator polynomial using the number of ECC words. - using (var generatorPolynom = CalculateGeneratorPolynom(eccInfo.ECCPerBlock)) - { - //Calculate error correction words - codewordBlocks = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom); - int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; - AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom); - return codewordBlocks; - } - - void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom) - { - var groupLength = codewordsInGroup * 8; - for (var i = 0; i < blocksInGroup; i++) - { - var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom); - codewordBlocks.Add(new CodewordBlock(offset2, groupLength, eccWordList)); - offset2 += groupLength; - } - } + //Calculate error correction words + codewordBlocks = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); + AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 0, bitArray.Length, generatorPolynom); + int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; + AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, offset, bitArray.Length - offset, generatorPolynom); + return codewordBlocks; } - // Calculate the length of the interleaved data - int CalculateInterleavedLength() + void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, int offset2, int count, Polynom generatorPolynom) { - var length = 0; - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) - { - foreach (var codeBlock in codeWordWithECC) - if ((uint)codeBlock.CodeWordsLength / 8 > i) - length += 8; - } - for (var i = 0; i < eccInfo.ECCPerBlock; i++) + var groupLength = codewordsInGroup * 8; + for (var i = 0; i < blocksInGroup; i++) { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Length > i) - length += 8; + var eccWordList = CalculateECCWords(bitArray, offset2, groupLength, eccInfo, generatorPolynom); + codewordBlocks.Add(new CodewordBlock(offset2, groupLength, eccWordList)); + offset2 += groupLength; } - length += remainderBits[version - 1]; - return length; } + } - // Interleave the data - BitArray InterleaveData() + // Calculate the length of the interleaved data + int CalculateInterleavedLength() + { + var length = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) { - var data = new BitArray(interleavedLength); - int pos = 0; - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) - { - foreach (var codeBlock in codeWordWithECC) - { - if ((uint)codeBlock.CodeWordsLength / 8 > i) - pos = bitArray.CopyTo(data, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8); - } - } - for (var i = 0; i < eccInfo.ECCPerBlock; i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Length > i) - pos = DecToBin(codeBlock.ECCWords[i], 8, data, pos); - } - - return data; + foreach (var codeBlock in codeWordWithECC) + if ((uint)codeBlock.CodeWordsLength / 8 > i) + length += 8; } - - // Place the modules on the QR code matrix - QRCodeData PlaceModules() + for (var i = 0; i < eccInfo.ECCPerBlock; i++) { - var qr = new QRCodeData(version, true); - var size = qr.ModuleMatrix.Count - 8; - var tempBitArray = new BitArray(18); //version string requires 18 bits - using (var blockedModules = new ModulePlacer.BlockedModules(size)) - { - ModulePlacer.PlaceFinderPatterns(qr, blockedModules); - ModulePlacer.ReserveSeperatorAreas(size, blockedModules); - ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules); - ModulePlacer.PlaceTimingPatterns(qr, blockedModules); - ModulePlacer.PlaceDarkModule(qr, version, blockedModules); - ModulePlacer.ReserveVersionAreas(size, version, blockedModules); - ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules); - var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel); - GetFormatString(tempBitArray, eccLevel, maskVersion); - ModulePlacer.PlaceFormat(qr, tempBitArray, true); - } + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + length += 8; + } + length += _remainderBits[version - 1]; + return length; + } - if (version >= 7) + // Interleave the data + BitArray InterleaveData() + { + var data = new BitArray(interleavedLength); + int pos = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) + { + foreach (var codeBlock in codeWordWithECC) { - GetVersionString(tempBitArray, version); - ModulePlacer.PlaceVersion(qr, tempBitArray, true); + if ((uint)codeBlock.CodeWordsLength / 8 > i) + pos = bitArray.CopyTo(data, (int)((uint)i * 8) + codeBlock.CodeWordsOffset, pos, 8); } - - return qr; } + for (var i = 0; i < eccInfo.ECCPerBlock; i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + pos = DecToBin(codeBlock.ECCWords[i], 8, data, pos); + } + + return data; } - private static readonly BitArray _getFormatGenerator = new BitArray(new bool[] { true, false, true, false, false, true, true, false, true, true, true }); - private static readonly BitArray _getFormatMask = new BitArray(new bool[] { true, false, true, false, true, false, false, false, false, false, true, false, false, true, false }); - /// - /// Generates a BitArray containing the format string for a QR code based on the error correction level and mask pattern version. - /// The format string includes the error correction level, mask pattern version, and error correction coding. - /// - /// The to write to, or null to create a new one. - /// The error correction level to be encoded in the format string. - /// The mask pattern version to be encoded in the format string. - /// A BitArray containing the 15-bit format string used in QR code generation. - private static void GetFormatString(BitArray fStrEcc, ECCLevel level, int maskVersion) + // Place the modules on the QR code matrix + QRCodeData PlaceModules() { - fStrEcc.Length = 15; - fStrEcc.SetAll(false); - WriteEccLevelAndVersion(); + var qr = new QRCodeData(version, true); + var size = qr.ModuleMatrix.Count - 8; + var tempBitArray = new BitArray(18); //version string requires 18 bits + using (var blockedModules = new ModulePlacer.BlockedModules(size)) + { + ModulePlacer.PlaceFinderPatterns(qr, blockedModules); + ModulePlacer.ReserveSeperatorAreas(size, blockedModules); + ModulePlacer.PlaceAlignmentPatterns(qr, _alignmentPatternTable[version].PatternPositions, blockedModules); + ModulePlacer.PlaceTimingPatterns(qr, blockedModules); + ModulePlacer.PlaceDarkModule(qr, version, blockedModules); + ModulePlacer.ReserveVersionAreas(size, version, blockedModules); + ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules); + var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel); + GetFormatString(tempBitArray, eccLevel, maskVersion); + ModulePlacer.PlaceFormat(qr, tempBitArray, true); + } - // Apply the format generator polynomial to add error correction to the format string. - int index = 0; - int count = 15; - TrimLeadingZeros(fStrEcc, ref index, ref count); - while (count > 10) + if (version >= 7) { - for (var i = 0; i < _getFormatGenerator.Length; i++) - fStrEcc[index + i] ^= _getFormatGenerator[i]; - TrimLeadingZeros(fStrEcc, ref index, ref count); + GetVersionString(tempBitArray, version); + ModulePlacer.PlaceVersion(qr, tempBitArray, true); } - // Align bits with the start of the array. - ShiftTowardsBit0(fStrEcc, index); + return qr; + } + } + + private static readonly BitArray _getFormatGenerator = new BitArray(new bool[] { true, false, true, false, false, true, true, false, true, true, true }); + private static readonly BitArray _getFormatMask = new BitArray(new bool[] { true, false, true, false, true, false, false, false, false, false, true, false, false, true, false }); + /// + /// Generates a BitArray containing the format string for a QR code based on the error correction level and mask pattern version. + /// The format string includes the error correction level, mask pattern version, and error correction coding. + /// + /// The to write to, or null to create a new one. + /// The error correction level to be encoded in the format string. + /// The mask pattern version to be encoded in the format string. + /// A BitArray containing the 15-bit format string used in QR code generation. + private static void GetFormatString(BitArray fStrEcc, ECCLevel level, int maskVersion) + { + fStrEcc.Length = 15; + fStrEcc.SetAll(false); + WriteEccLevelAndVersion(); + + // Apply the format generator polynomial to add error correction to the format string. + int index = 0; + int count = 15; + TrimLeadingZeros(fStrEcc, ref index, ref count); + while (count > 10) + { + for (var i = 0; i < _getFormatGenerator.Length; i++) + fStrEcc[index + i] ^= _getFormatGenerator[i]; + TrimLeadingZeros(fStrEcc, ref index, ref count); + } - // Prefix the error correction bits with the ECC level and version number. - fStrEcc.Length = 10 + 5; - ShiftAwayFromBit0(fStrEcc, (10 - count) + 5); - WriteEccLevelAndVersion(); + // Align bits with the start of the array. + ShiftTowardsBit0(fStrEcc, index); - // XOR the format string with a predefined mask to add robustness against errors. - fStrEcc.Xor(_getFormatMask); + // Prefix the error correction bits with the ECC level and version number. + fStrEcc.Length = 10 + 5; + ShiftAwayFromBit0(fStrEcc, (10 - count) + 5); + WriteEccLevelAndVersion(); - void WriteEccLevelAndVersion() + // XOR the format string with a predefined mask to add robustness against errors. + fStrEcc.Xor(_getFormatMask); + + void WriteEccLevelAndVersion() + { + switch (level) { - switch (level) - { - case ECCLevel.L: // 01 - fStrEcc[1] = true; - break; - case ECCLevel.H: // 10 - fStrEcc[0] = true; - break; - case ECCLevel.Q: // 11 - fStrEcc[0] = true; - fStrEcc[1] = true; - break; - default: // M: 00 - break; - } - - // Insert the 3-bit mask version directly after the error correction level bits. - DecToBin(maskVersion, 3, fStrEcc, 2); + case ECCLevel.L: // 01 + fStrEcc[1] = true; + break; + case ECCLevel.H: // 10 + fStrEcc[0] = true; + break; + case ECCLevel.Q: // 11 + fStrEcc[0] = true; + fStrEcc[1] = true; + break; + default: // M: 00 + break; } + + // Insert the 3-bit mask version directly after the error correction level bits. + DecToBin(maskVersion, 3, fStrEcc, 2); } + } #if !NETFRAMEWORK || NET45_OR_GREATER - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count) + private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count) + { + while (count > 0 && !fStrEcc[index]) { - while (count > 0 && !fStrEcc[index]) - { - index++; - count--; - } + index++; + count--; } + } - private static void ShiftTowardsBit0(BitArray fStrEcc, int num) - { + private static void ShiftTowardsBit0(BitArray fStrEcc, int num) + { #if NETCOREAPP - fStrEcc.RightShift(num); // Shift towards bit 0 + fStrEcc.RightShift(num); // Shift towards bit 0 #else - for (var i = 0; i < fStrEcc.Length - num; i++) - fStrEcc[i] = fStrEcc[i + num]; - for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++) - fStrEcc[i] = false; + for (var i = 0; i < fStrEcc.Length - num; i++) + fStrEcc[i] = fStrEcc[i + num]; + for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++) + fStrEcc[i] = false; #endif - } + } - private static void ShiftAwayFromBit0(BitArray fStrEcc, int num) - { + private static void ShiftAwayFromBit0(BitArray fStrEcc, int num) + { #if NETCOREAPP - fStrEcc.LeftShift(num); // Shift away from bit 0 + fStrEcc.LeftShift(num); // Shift away from bit 0 #else - for (var i = fStrEcc.Length - 1; i >= num; i--) - fStrEcc[i] = fStrEcc[i - num]; - for (var i = 0; i < num; i++) - fStrEcc[i] = false; + for (var i = fStrEcc.Length - 1; i >= num; i--) + fStrEcc[i] = fStrEcc[i - num]; + for (var i = 0; i < num; i++) + fStrEcc[i] = false; #endif - } + } - private static readonly BitArray _getVersionGenerator = new BitArray(new bool[] { true, true, true, true, true, false, false, true, false, false, true, false, true }); + private static readonly BitArray _getVersionGenerator = new BitArray(new bool[] { true, true, true, true, true, false, false, true, false, false, true, false, true }); - /// - /// Encodes the version information of a QR code into a BitArray using error correction coding similar to format information encoding. - /// This method is used for QR codes version 7 and above. - /// - /// A to write the version string to. - /// The version number of the QR code (7-40). - /// A BitArray containing the encoded version information, which includes error correction bits. - private static void GetVersionString(BitArray vStr, int version) - { - vStr.Length = 18; - vStr.SetAll(false); - DecToBin(version, 6, vStr, 0); // Convert the version number to a 6-bit binary representation. + /// + /// Encodes the version information of a QR code into a BitArray using error correction coding similar to format information encoding. + /// This method is used for QR codes version 7 and above. + /// + /// A to write the version string to. + /// The version number of the QR code (7-40). + /// A BitArray containing the encoded version information, which includes error correction bits. + private static void GetVersionString(BitArray vStr, int version) + { + vStr.Length = 18; + vStr.SetAll(false); + DecToBin(version, 6, vStr, 0); // Convert the version number to a 6-bit binary representation. - var count = vStr.Length; - var index = 0; - TrimLeadingZeros(vStr, ref index, ref count); // Trim leading zeros to normalize the version bit sequence. + var count = vStr.Length; + var index = 0; + TrimLeadingZeros(vStr, ref index, ref count); // Trim leading zeros to normalize the version bit sequence. - // Perform error correction encoding using a polynomial generator (specified by _getVersionGenerator). - while (count > 12) // The target length of the version information error correction information is 12 bits. - { - for (var i = 0; i < _getVersionGenerator.Length; i++) - vStr[index + i] ^= _getVersionGenerator[i]; // XOR the current bits with the generator sequence. + // Perform error correction encoding using a polynomial generator (specified by _getVersionGenerator). + while (count > 12) // The target length of the version information error correction information is 12 bits. + { + for (var i = 0; i < _getVersionGenerator.Length; i++) + vStr[index + i] ^= _getVersionGenerator[i]; // XOR the current bits with the generator sequence. - TrimLeadingZeros(vStr, ref index, ref count); // Trim leading zeros after each XOR operation to maintain the proper sequence. - } + TrimLeadingZeros(vStr, ref index, ref count); // Trim leading zeros after each XOR operation to maintain the proper sequence. + } - ShiftTowardsBit0(vStr, index); // Align the bit array so the data starts at index 0. + ShiftTowardsBit0(vStr, index); // Align the bit array so the data starts at index 0. - // Prefix the error correction encoding with 6 bits containing the version number - vStr.Length = 12 + 6; - ShiftAwayFromBit0(vStr, (12 - count) + 6); - DecToBin(version, 6, vStr, 0); - } + // Prefix the error correction encoding with 6 bits containing the version number + vStr.Length = 12 + 6; + ShiftAwayFromBit0(vStr, (12 - count) + 6); + DecToBin(version, 6, vStr, 0); + } - /// - /// Calculates the Error Correction Codewords (ECC) for a segment of data using the provided ECC information. - /// This method applies polynomial division, using the message polynomial and a generator polynomial, - /// to compute the remainder which forms the ECC codewords. - /// - private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynomBase) + /// + /// Calculates the Error Correction Codewords (ECC) for a segment of data using the provided ECC information. + /// This method applies polynomial division, using the message polynomial and a generator polynomial, + /// to compute the remainder which forms the ECC codewords. + /// + private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo, Polynom generatorPolynomBase) + { + var eccWords = eccInfo.ECCPerBlock; + // Calculate the message polynomial from the bit array data. + var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); + var generatorPolynom = generatorPolynomBase.Clone(); + + // Adjust the exponents in the message polynomial to account for ECC length. + for (var i = 0; i < messagePolynom.Count; i++) + messagePolynom[i] = new PolynomItem(messagePolynom[i].Coefficient, + messagePolynom[i].Exponent + eccWords); + + // Adjust the generator polynomial exponents based on the message polynomial. + for (var i = 0; i < generatorPolynom.Count; i++) + generatorPolynom[i] = new PolynomItem(generatorPolynom[i].Coefficient, + generatorPolynom[i].Exponent + (messagePolynom.Count - 1)); + + // Divide the message polynomial by the generator polynomial to find the remainder. + var leadTermSource = messagePolynom; + for (var i = 0; (leadTermSource.Count > 0 && leadTermSource[leadTermSource.Count - 1].Exponent > 0); i++) { - var eccWords = eccInfo.ECCPerBlock; - // Calculate the message polynomial from the bit array data. - var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); - var generatorPolynom = generatorPolynomBase.Clone(); - - // Adjust the exponents in the message polynomial to account for ECC length. - for (var i = 0; i < messagePolynom.Count; i++) - messagePolynom[i] = new PolynomItem(messagePolynom[i].Coefficient, - messagePolynom[i].Exponent + eccWords); - - // Adjust the generator polynomial exponents based on the message polynomial. - for (var i = 0; i < generatorPolynom.Count; i++) - generatorPolynom[i] = new PolynomItem(generatorPolynom[i].Coefficient, - generatorPolynom[i].Exponent + (messagePolynom.Count - 1)); - - // Divide the message polynomial by the generator polynomial to find the remainder. - var leadTermSource = messagePolynom; - for (var i = 0; (leadTermSource.Count > 0 && leadTermSource[leadTermSource.Count - 1].Exponent > 0); i++) + if (leadTermSource[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. { - if (leadTermSource[0].Coefficient == 0) // Simplify the polynomial if the leading coefficient is zero. - { - leadTermSource.RemoveAt(0); - leadTermSource.Add(new PolynomItem(0, leadTermSource[leadTermSource.Count - 1].Exponent - 1)); - } - else // Otherwise, perform polynomial reduction using XOR and multiplication with the generator polynomial. - { - // Convert the first coefficient to its corresponding alpha exponent unless it's zero. - // Coefficients that are zero remain zero because log(0) is undefined. - var index0Coefficient = leadTermSource[0].Coefficient; - index0Coefficient = index0Coefficient == 0 ? 0 : GetAlphaExpFromIntVal(index0Coefficient); - var alphaNotation = new PolynomItem(index0Coefficient, leadTermSource[0].Exponent); - var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, alphaNotation, i); - ConvertToDecNotationInPlace(resPoly); - var newPoly = XORPolynoms(leadTermSource, resPoly); - // Free memory used by the previous polynomials. - resPoly.Dispose(); - leadTermSource.Dispose(); - // Update the message polynomial with the new remainder. - leadTermSource = newPoly; - } + leadTermSource.RemoveAt(0); + leadTermSource.Add(new PolynomItem(0, leadTermSource[leadTermSource.Count - 1].Exponent - 1)); } + else // Otherwise, perform polynomial reduction using XOR and multiplication with the generator polynomial. + { + // Convert the first coefficient to its corresponding alpha exponent unless it's zero. + // Coefficients that are zero remain zero because log(0) is undefined. + var index0Coefficient = leadTermSource[0].Coefficient; + index0Coefficient = index0Coefficient == 0 ? 0 : GetAlphaExpFromIntVal(index0Coefficient); + var alphaNotation = new PolynomItem(index0Coefficient, leadTermSource[0].Exponent); + var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, alphaNotation, i); + ConvertToDecNotationInPlace(resPoly); + var newPoly = XORPolynoms(leadTermSource, resPoly); + // Free memory used by the previous polynomials. + resPoly.Dispose(); + leadTermSource.Dispose(); + // Update the message polynomial with the new remainder. + leadTermSource = newPoly; + } + } - // Free memory used by the generator polynomial. - generatorPolynom.Dispose(); + // Free memory used by the generator polynomial. + generatorPolynom.Dispose(); - // Convert the resulting polynomial into a byte array representing the ECC codewords. - var ret = new byte[leadTermSource.Count]; - for (var i = 0; i < leadTermSource.Count; i++) - ret[i] = (byte)leadTermSource[i].Coefficient; + // Convert the resulting polynomial into a byte array representing the ECC codewords. + var ret = new byte[leadTermSource.Count]; + for (var i = 0; i < leadTermSource.Count; i++) + ret[i] = (byte)leadTermSource[i].Coefficient; - // Free memory used by the message polynomial. - leadTermSource.Dispose(); + // Free memory used by the message polynomial. + leadTermSource.Dispose(); - return ret; - } + return ret; + } - /// - /// Converts all polynomial item coefficients from their alpha exponent notation to decimal representation in place. - /// This conversion facilitates operations that require polynomial coefficients in their integer forms. - /// - private static void ConvertToDecNotationInPlace(Polynom poly) + /// + /// Converts all polynomial item coefficients from their alpha exponent notation to decimal representation in place. + /// This conversion facilitates operations that require polynomial coefficients in their integer forms. + /// + private static void ConvertToDecNotationInPlace(Polynom poly) + { + for (var i = 0; i < poly.Count; i++) { - for (var i = 0; i < poly.Count; i++) - { - // Convert the alpha exponent of the coefficient to its decimal value and create a new polynomial item with the updated coefficient. - poly[i] = new PolynomItem(GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); - } + // Convert the alpha exponent of the coefficient to its decimal value and create a new polynomial item with the updated coefficient. + poly[i] = new PolynomItem(GetIntValFromAlphaExp(poly[i].Coefficient), poly[i].Exponent); } + } - /// - /// Determines the minimum QR code version required to encode a given amount of data with a specific encoding mode and error correction level. - /// If no suitable version is found, it throws an exception indicating that the data length exceeds the maximum capacity for the given settings. - /// - /// The length of the data to be encoded. - /// The encoding mode (e.g., Numeric, Alphanumeric, Byte). - /// The error correction level (e.g., Low, Medium, Quartile, High). - /// The minimum version of the QR code that can accommodate the given data and settings. - private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) + /// + /// Determines the minimum QR code version required to encode a given amount of data with a specific encoding mode and error correction level. + /// If no suitable version is found, it throws an exception indicating that the data length exceeds the maximum capacity for the given settings. + /// + /// The length of the data to be encoded. + /// The encoding mode (e.g., Numeric, Alphanumeric, Byte). + /// The error correction level (e.g., Low, Medium, Quartile, High). + /// The minimum version of the QR code that can accommodate the given data and settings. + private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) + { + // capacity table is already sorted by version number ascending, so the smallest version that can hold the data is the first one found + foreach (var x in _capacityTable) { - // capacity table is already sorted by version number ascending, so the smallest version that can hold the data is the first one found - foreach (var x in capacityTable) + // find the requested ECC level and encoding mode in the capacity table + foreach (var y in x.Details) { - // find the requested ECC level and encoding mode in the capacity table - foreach (var y in x.Details) + if (y.ErrorCorrectionLevel == eccLevel && y.CapacityDict[encMode] >= length) { - if (y.ErrorCorrectionLevel == eccLevel && y.CapacityDict[encMode] >= length) - { - // if the capacity of the current version is enough, return the version number - return x.Version; - } + // if the capacity of the current version is enough, return the version number + return x.Version; } } - - // if no version was found, throw an exception - var maxSizeByte = capacityTable.Where( - x => x.Details.Any( - y => (y.ErrorCorrectionLevel == eccLevel)) - ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); - throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); } - /// - /// Determines the most efficient encoding mode for the given plain text based on its character content - /// and a flag indicating whether to force UTF-8 encoding. - /// - private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) + // if no version was found, throw an exception + var maxSizeByte = _capacityTable.Where( + x => x.Details.Any( + y => (y.ErrorCorrectionLevel == eccLevel)) + ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); + throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); + } + + /// + /// Determines the most efficient encoding mode for the given plain text based on its character content + /// and a flag indicating whether to force UTF-8 encoding. + /// + private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) + { + if (forceUtf8) + return EncodingMode.Byte; + var result = EncodingMode.Numeric; // assume numeric + foreach (char c in plainText) { - if (forceUtf8) return EncodingMode.Byte; - EncodingMode result = EncodingMode.Numeric; // assume numeric - foreach (char c in plainText) - { - if (IsInRange(c, '0', '9')) continue; // numeric - char.IsDigit() for Latin1 - result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric - if (IsInRange(c, 'A', 'Z') || alphanumEncTable.Contains(c)) continue; // alphanumeric - return EncodingMode.Byte; // not numeric or alphanumeric, assume byte - } - return result; // either numeric or alphanumeric + if (IsInRange(c, '0', '9')) + continue; // numeric - char.IsDigit() for Latin1 + result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric + if (IsInRange(c, 'A', 'Z') || _alphanumEncTable.Contains(c)) + continue; // alphanumeric + return EncodingMode.Byte; // not numeric or alphanumeric, assume byte } + return result; // either numeric or alphanumeric + } - /// - /// Checks if a character falls within a specified range. - /// - private static bool IsInRange(char c, char min, char max) + /// + /// Checks if a character falls within a specified range. + /// + private static bool IsInRange(char c, char min, char max) + => (uint)(c - min) <= (uint)(max - min); + + /// + /// Calculates the message polynomial from a bit array which represents the encoded data. + /// + /// A polynomial representation of the message. + private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int count) + { + var messagePol = new Polynom(count /= 8); + for (var i = count - 1; i >= 0; i--) { - return (uint)(c - min) <= (uint)(max - min); + messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); + offset += 8; } + return messagePol; + } + + /// + /// Calculates the generator polynomial used for creating error correction codewords. + /// + /// The number of error correction codewords to generate. + /// A polynomial that can be used to generate ECC codewords. + private static Polynom CalculateGeneratorPolynom(int numEccWords) + { + var generatorPolynom = new Polynom(2); // Start with the simplest form of the polynomial + generatorPolynom.Add(new PolynomItem(0, 1)); + generatorPolynom.Add(new PolynomItem(0, 0)); - /// - /// Calculates the message polynomial from a bit array which represents the encoded data. - /// - /// A polynomial representation of the message. - private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int count) + using (var multiplierPolynom = new Polynom(numEccWords * 2)) // Used for polynomial multiplication { - var messagePol = new Polynom(count /= 8); - for (var i = count - 1; i >= 0; i--) + for (var i = 1; i <= numEccWords - 1; i++) { - messagePol.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); - offset += 8; + // Clear and set up the multiplier polynomial for the current multiplication + multiplierPolynom.Clear(); + multiplierPolynom.Add(new PolynomItem(0, 1)); + multiplierPolynom.Add(new PolynomItem(i, 0)); + + // Multiply the generator polynomial by the current multiplier polynomial + var newGeneratorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + generatorPolynom.Dispose(); + generatorPolynom = newGeneratorPolynom; } - return messagePol; } - /// - /// Calculates the generator polynomial used for creating error correction codewords. - /// - /// The number of error correction codewords to generate. - /// A polynomial that can be used to generate ECC codewords. - private static Polynom CalculateGeneratorPolynom(int numEccWords) - { - var generatorPolynom = new Polynom(2); // Start with the simplest form of the polynomial - generatorPolynom.Add(new PolynomItem(0, 1)); - generatorPolynom.Add(new PolynomItem(0, 0)); - - using (var multiplierPolynom = new Polynom(numEccWords * 2)) // Used for polynomial multiplication - { - for (var i = 1; i <= numEccWords - 1; i++) - { - // Clear and set up the multiplier polynomial for the current multiplication - multiplierPolynom.Clear(); - multiplierPolynom.Add(new PolynomItem(0, 1)); - multiplierPolynom.Add(new PolynomItem(i, 0)); - - // Multiply the generator polynomial by the current multiplier polynomial - var newGeneratorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); - generatorPolynom.Dispose(); - generatorPolynom = newGeneratorPolynom; - } - } + return generatorPolynom; // Return the completed generator polynomial + } - return generatorPolynom; // Return the completed generator polynomial + /// + /// Converts a segment of a BitArray into its decimal (integer) equivalent. + /// + /// The integer value that represents the specified binary data. + private static int BinToDec(BitArray bitArray, int offset, int count) + { + var ret = 0; + for (int i = 0; i < count; i++) + { + ret ^= bitArray[offset + i] ? 1 << (count - i - 1) : 0; } + return ret; + } - /// - /// Converts a segment of a BitArray into its decimal (integer) equivalent. - /// - /// The integer value that represents the specified binary data. - private static int BinToDec(BitArray bitArray, int offset, int count) + /// + /// Converts a decimal number to binary and stores the result in a BitArray starting from a specific index. + /// + /// The decimal number to convert to binary. + /// The number of bits to use for the binary representation (ensuring fixed-width like 8, 16, 32 bits). + /// The BitArray where the binary bits will be stored. + /// The starting index in the BitArray where the bits will be stored. + /// The next index in the BitArray after the last bit placed. + private static int DecToBin(int decNum, int bits, BitArray bitList, int index) + { + // Convert decNum to binary using a bitwise operation + for (int i = bits - 1; i >= 0; i--) { - var ret = 0; - for (int i = 0; i < count; i++) - { - ret ^= bitArray[offset + i] ? 1 << (count - i - 1) : 0; - } - return ret; + // Check each bit from most significant to least significant + bool bit = (decNum & (1 << i)) != 0; + bitList[index++] = bit; } + return index; + } - /// - /// Converts a decimal number to binary and stores the result in a BitArray starting from a specific index. - /// - /// The decimal number to convert to binary. - /// The number of bits to use for the binary representation (ensuring fixed-width like 8, 16, 32 bits). - /// The BitArray where the binary bits will be stored. - /// The starting index in the BitArray where the bits will be stored. - /// The next index in the BitArray after the last bit placed. - private static int DecToBin(int decNum, int bits, BitArray bitList, int index) + /// + /// Determines the number of bits used to indicate the count of characters in a segment, depending on the QR code version and the encoding mode. + /// + /// The version of the QR code, which influences the number of bits due to increasing data capacity. + /// The encoding mode (e.g., Numeric, Alphanumeric, Byte) used for the data segment. + /// The number of bits needed to represent the character count in the specified encoding mode and version. + private static int GetCountIndicatorLength(int version, EncodingMode encMode) + { + // Different versions and encoding modes require different lengths of bits to represent the character count efficiently + if (version < 10) { - // Convert decNum to binary using a bitwise operation - for (int i = bits - 1; i >= 0; i--) - { - // Check each bit from most significant to least significant - bool bit = (decNum & (1 << i)) != 0; - bitList[index++] = bit; - } - return index; + if (encMode == EncodingMode.Numeric) + return 10; + else if (encMode == EncodingMode.Alphanumeric) + return 9; + else + return 8; } - - /// - /// Determines the number of bits used to indicate the count of characters in a segment, depending on the QR code version and the encoding mode. - /// - /// The version of the QR code, which influences the number of bits due to increasing data capacity. - /// The encoding mode (e.g., Numeric, Alphanumeric, Byte) used for the data segment. - /// The number of bits needed to represent the character count in the specified encoding mode and version. - private static int GetCountIndicatorLength(int version, EncodingMode encMode) + else if (version < 27) { - // Different versions and encoding modes require different lengths of bits to represent the character count efficiently - if (version < 10) - { - if (encMode == EncodingMode.Numeric) - return 10; - else if (encMode == EncodingMode.Alphanumeric) - return 9; - else - return 8; - } - else if (version < 27) - { - if (encMode == EncodingMode.Numeric) - return 12; - else if (encMode == EncodingMode.Alphanumeric) - return 11; - else if (encMode == EncodingMode.Byte) - return 16; - else - return 10; - } + if (encMode == EncodingMode.Numeric) + return 12; + else if (encMode == EncodingMode.Alphanumeric) + return 11; + else if (encMode == EncodingMode.Byte) + return 16; else - { - if (encMode == EncodingMode.Numeric) - return 14; - else if (encMode == EncodingMode.Alphanumeric) - return 13; - else if (encMode == EncodingMode.Byte) - return 16; - else - return 12; - } + return 10; } - - /// - /// Calculates the data length based on the encoding mode, text content, and whether UTF-8 is forced. - /// - /// The encoding mode used for the QR code data. - /// The plain text input to be encoded. - /// A BitArray representing the binary data of the encoded text. - /// Flag to determine if UTF-8 encoding should be enforced. - /// The length of data in units appropriate to the encoding (bytes or characters). - private static int GetDataLength(EncodingMode encoding, string plainText, BitArray codedText, bool forceUtf8) + else { - // If UTF-8 is forced or the text is detected as UTF-8, return the number of bytes, otherwise return the character count. - return forceUtf8 || IsUtf8() ? (int)((uint)codedText.Length / 8) : plainText.Length; - - bool IsUtf8() - { - return (encoding == EncodingMode.Byte && (forceUtf8 || !IsValidISO(plainText))); - } + if (encMode == EncodingMode.Numeric) + return 14; + else if (encMode == EncodingMode.Alphanumeric) + return 13; + else if (encMode == EncodingMode.Byte) + return 16; + else + return 12; } + } - private static readonly Encoding _iso88591ExceptionFallback = Encoding.GetEncoding(28591, new EncoderExceptionFallback(), new DecoderExceptionFallback()); // ISO-8859-1 - /// - /// Checks if the given string can be accurately represented and retrieved in ISO-8859-1 encoding. - /// - private static bool IsValidISO(string input) + /// + /// Calculates the data length based on the encoding mode, text content, and whether UTF-8 is forced. + /// + /// The encoding mode used for the QR code data. + /// The plain text input to be encoded. + /// A BitArray representing the binary data of the encoded text. + /// Flag to determine if UTF-8 encoding should be enforced. + /// The length of data in units appropriate to the encoding (bytes or characters). + private static int GetDataLength(EncodingMode encoding, string plainText, BitArray codedText, bool forceUtf8) + { + // If UTF-8 is forced or the text is detected as UTF-8, return the number of bytes, otherwise return the character count. + return forceUtf8 || IsUtf8() ? (int)((uint)codedText.Length / 8) : plainText.Length; + + bool IsUtf8() => (encoding == EncodingMode.Byte && (forceUtf8 || !IsValidISO(plainText))); + } + + private static readonly Encoding _iso88591ExceptionFallback = Encoding.GetEncoding(28591, new EncoderExceptionFallback(), new DecoderExceptionFallback()); // ISO-8859-1 + /// + /// Checks if the given string can be accurately represented and retrieved in ISO-8859-1 encoding. + /// + private static bool IsValidISO(string input) + { + // No heap allocations if the string is ISO-8859-1 + try { - // No heap allocations if the string is ISO-8859-1 - try - { - _ = _iso88591ExceptionFallback.GetByteCount(input); - return true; - } - catch (EncoderFallbackException) // The exception is a heap allocation and not ideal - { - return false; - } + _ = _iso88591ExceptionFallback.GetByteCount(input); + return true; } - - /// - /// Converts plain text to a binary format suitable for QR code generation, based on the specified encoding mode. - /// - /// The text to be encoded. - /// The encoding mode. - /// The ECI mode specifying the character encoding to use. - /// Flag indicating whether to prepend a UTF-8 Byte Order Mark. - /// Flag indicating whether UTF-8 encoding is forced. - /// A BitArray containing the binary representation of the encoded data. - private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode, EciMode eciMode, bool utf8BOM, bool forceUtf8) + catch (EncoderFallbackException) // The exception is a heap allocation and not ideal { - switch (encMode) - { - case EncodingMode.Alphanumeric: - return PlainTextToBinaryAlphanumeric(plainText); - case EncodingMode.Numeric: - return PlainTextToBinaryNumeric(plainText); - case EncodingMode.Byte: - return PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8); - case EncodingMode.Kanji: - case EncodingMode.ECI: - default: - return _emptyBitArray; - } + return false; } + } - private static readonly BitArray _emptyBitArray = new BitArray(0); - - /// - /// Converts numeric plain text into a binary format specifically optimized for QR codes. - /// Numeric compression groups up to 3 digits into 10 bits, less for remaining digits if they do not complete a group of three. - /// - /// The numeric text to be encoded, which should only contain digit characters. - /// A BitArray representing the binary data of the encoded numeric text. - private static BitArray PlainTextToBinaryNumeric(string plainText) + /// + /// Converts plain text to a binary format suitable for QR code generation, based on the specified encoding mode. + /// + /// The text to be encoded. + /// The encoding mode. + /// The ECI mode specifying the character encoding to use. + /// Flag indicating whether to prepend a UTF-8 Byte Order Mark. + /// Flag indicating whether UTF-8 encoding is forced. + /// A BitArray containing the binary representation of the encoded data. + private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode, EciMode eciMode, bool utf8BOM, bool forceUtf8) + { + return encMode switch { - // Calculate the length of the BitArray needed to encode the text. - // Groups of three digits are encoded in 10 bits, remaining groups of two or one digits take 7 or 4 bits respectively. - var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); - var index = 0; + EncodingMode.Alphanumeric => PlainTextToBinaryAlphanumeric(plainText), + EncodingMode.Numeric => PlainTextToBinaryNumeric(plainText), + EncodingMode.Byte => PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8), + _ => _emptyBitArray, + }; + } - // Process each group of three digits. - for (int i = 0; i < plainText.Length - 2; i += 3) - { - // Parse the next three characters as a decimal integer. + private static readonly BitArray _emptyBitArray = new BitArray(0); + + /// + /// Converts numeric plain text into a binary format specifically optimized for QR codes. + /// Numeric compression groups up to 3 digits into 10 bits, less for remaining digits if they do not complete a group of three. + /// + /// The numeric text to be encoded, which should only contain digit characters. + /// A BitArray representing the binary data of the encoded numeric text. + private static BitArray PlainTextToBinaryNumeric(string plainText) + { + // Calculate the length of the BitArray needed to encode the text. + // Groups of three digits are encoded in 10 bits, remaining groups of two or one digits take 7 or 4 bits respectively. + var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); + var index = 0; + + // Process each group of three digits. + for (int i = 0; i < plainText.Length - 2; i += 3) + { + // Parse the next three characters as a decimal integer. #if NET5_0_OR_GREATER - var dec = int.Parse(plainText.AsSpan(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); #endif - // Convert the decimal to binary and store it in the BitArray. - index = DecToBin(dec, 10, bitArray, index); - } + // Convert the decimal to binary and store it in the BitArray. + index = DecToBin(dec, 10, bitArray, index); + } - // Handle any remaining digits if the total number is not a multiple of three. - if (plainText.Length % 3 == 2) // Two remaining digits are encoded in 7 bits. - { + // Handle any remaining digits if the total number is not a multiple of three. + if (plainText.Length % 3 == 2) // Two remaining digits are encoded in 7 bits. + { #if NET5_0_OR_GREATER - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 7, bitArray, index); - } - else if (plainText.Length % 3 == 1) // One remaining digit is encoded in 4 bits. - { + index = DecToBin(dec, 7, bitArray, index); + } + else if (plainText.Length % 3 == 1) // One remaining digit is encoded in 4 bits. + { #if NET5_0_OR_GREATER - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 4, bitArray, index); - } - - return bitArray; + index = DecToBin(dec, 4, bitArray, index); } - /// - /// Converts alphanumeric plain text into a binary format optimized for QR codes. - /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, - /// and 6 bits for a single remaining character if the total count is odd. - /// - /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// A BitArray representing the binary data of the encoded alphanumeric text. - private static BitArray PlainTextToBinaryAlphanumeric(string plainText) - { - // Calculate the length of the BitArray needed based on the number of character pairs. - var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6); - var codeIndex = 0; - var index = 0; - var count = plainText.Length; - - // Process each pair of characters. - while (count >= 2) - { - // Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating. - var dec = alphanumEncDict[plainText[index++]] * 45 + alphanumEncDict[plainText[index++]]; - // Convert the number to binary and store it in the BitArray. - codeIndex = DecToBin(dec, 11, codeText, codeIndex); - count -= 2; - } + return bitArray; + } - // Handle the last character if the length is odd. - if (count > 0) - { - DecToBin(alphanumEncDict[plainText[index]], 6, codeText, codeIndex); - } + /// + /// Converts alphanumeric plain text into a binary format optimized for QR codes. + /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, + /// and 6 bits for a single remaining character if the total count is odd. + /// + /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. + /// A BitArray representing the binary data of the encoded alphanumeric text. + private static BitArray PlainTextToBinaryAlphanumeric(string plainText) + { + // Calculate the length of the BitArray needed based on the number of character pairs. + var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6); + var codeIndex = 0; + var index = 0; + var count = plainText.Length; + + // Process each pair of characters. + while (count >= 2) + { + // Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating. + var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]]; + // Convert the number to binary and store it in the BitArray. + codeIndex = DecToBin(dec, 11, codeText, codeIndex); + count -= 2; + } - return codeText; + // Handle the last character if the length is odd. + if (count > 0) + { + DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex); } - private static readonly Encoding _iso8859_1 = + return codeText; + } + + private static readonly Encoding _iso8859_1 = #if NET5_0_OR_GREATER - Encoding.Latin1; + Encoding.Latin1; #else - Encoding.GetEncoding(28591); // ISO-8859-1 + Encoding.GetEncoding(28591); // ISO-8859-1 #endif - private static Encoding? _iso8859_2; - - /// - /// Converts plain text into a binary format using byte mode encoding, which supports various character encodings through ECI (Extended Channel Interpretations). - /// - /// The text to be encoded. - /// The ECI mode that specifies the character encoding to use. - /// Specifies whether to include a Byte Order Mark (BOM) for UTF-8 encoding. - /// Forces UTF-8 encoding regardless of the text content's compatibility with ISO-8859-1. - /// A BitArray representing the binary data of the encoded text. - /// - /// The returned text is always encoded as ISO-8859-1 unless either the text contains a non-ISO-8859-1 character or - /// UTF-8 encoding is forced. This does not meet the QR Code standard, which requires the use of ECI to specify the encoding - /// when not ISO-8859-1. - /// - private static BitArray PlainTextToBinaryByte(string plainText, EciMode eciMode, bool utf8BOM, bool forceUtf8) - { - Encoding targetEncoding; + private static Encoding? _iso8859_2; - // Check if the text is valid ISO-8859-1 and UTF-8 is not forced, then encode using ISO-8859-1. - if (IsValidISO(plainText) && !forceUtf8) - { - targetEncoding = _iso8859_1; - utf8BOM = false; - } - else + /// + /// Converts plain text into a binary format using byte mode encoding, which supports various character encodings through ECI (Extended Channel Interpretations). + /// + /// The text to be encoded. + /// The ECI mode that specifies the character encoding to use. + /// Specifies whether to include a Byte Order Mark (BOM) for UTF-8 encoding. + /// Forces UTF-8 encoding regardless of the text content's compatibility with ISO-8859-1. + /// A BitArray representing the binary data of the encoded text. + /// + /// The returned text is always encoded as ISO-8859-1 unless either the text contains a non-ISO-8859-1 character or + /// UTF-8 encoding is forced. This does not meet the QR Code standard, which requires the use of ECI to specify the encoding + /// when not ISO-8859-1. + /// + private static BitArray PlainTextToBinaryByte(string plainText, EciMode eciMode, bool utf8BOM, bool forceUtf8) + { + Encoding targetEncoding; + + // Check if the text is valid ISO-8859-1 and UTF-8 is not forced, then encode using ISO-8859-1. + if (IsValidISO(plainText) && !forceUtf8) + { + targetEncoding = _iso8859_1; + utf8BOM = false; + } + else + { + // Determine the encoding based on the specified ECI mode. + switch (eciMode) { - // Determine the encoding based on the specified ECI mode. - switch (eciMode) - { - case EciMode.Iso8859_1: - // Convert text to ISO-8859-1 and encode. - targetEncoding = _iso8859_1; - utf8BOM = false; - break; - case EciMode.Iso8859_2: - // Note: ISO-8859-2 is not natively supported on .NET Core - // - // Users must install the System.Text.Encoding.CodePages package and call Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) - // before using this encoding mode. - _iso8859_2 ??= Encoding.GetEncoding(28592); // ISO-8859-2 - // Convert text to ISO-8859-2 and encode. - targetEncoding = _iso8859_2; - utf8BOM = false; - break; - case EciMode.Default: - case EciMode.Utf8: - default: - // Handle UTF-8 encoding, optionally adding a BOM if specified. - targetEncoding = Encoding.UTF8; - break; - } + case EciMode.Iso8859_1: + // Convert text to ISO-8859-1 and encode. + targetEncoding = _iso8859_1; + utf8BOM = false; + break; + case EciMode.Iso8859_2: + // Note: ISO-8859-2 is not natively supported on .NET Core + // + // Users must install the System.Text.Encoding.CodePages package and call Encoding.RegisterProvider(CodePagesEncodingProvider.Instance) + // before using this encoding mode. + _iso8859_2 ??= Encoding.GetEncoding(28592); // ISO-8859-2 + // Convert text to ISO-8859-2 and encode. + targetEncoding = _iso8859_2; + utf8BOM = false; + break; + case EciMode.Default: + case EciMode.Utf8: + default: + // Handle UTF-8 encoding, optionally adding a BOM if specified. + targetEncoding = Encoding.UTF8; + break; } + } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1 - // We can use stackalloc for small arrays to prevent heap allocations - const int MAX_STACK_SIZE_IN_BYTES = 512; - - int count = targetEncoding.GetByteCount(plainText); - byte[]? bufferFromPool = null; - Span codeBytes = (count <= MAX_STACK_SIZE_IN_BYTES) - ? (stackalloc byte[MAX_STACK_SIZE_IN_BYTES]) - : (bufferFromPool = ArrayPool.Shared.Rent(count)); - codeBytes = codeBytes.Slice(0, count); - targetEncoding.GetBytes(plainText, codeBytes); + // We can use stackalloc for small arrays to prevent heap allocations + const int MAX_STACK_SIZE_IN_BYTES = 512; + + int count = targetEncoding.GetByteCount(plainText); + byte[]? bufferFromPool = null; + Span codeBytes = (count <= MAX_STACK_SIZE_IN_BYTES) + ? (stackalloc byte[MAX_STACK_SIZE_IN_BYTES]) + : (bufferFromPool = ArrayPool.Shared.Rent(count)); + codeBytes = codeBytes.Slice(0, count); + targetEncoding.GetBytes(plainText, codeBytes); #else - byte[] codeBytes = targetEncoding.GetBytes(plainText); + byte[] codeBytes = targetEncoding.GetBytes(plainText); #endif - // Convert the array of bytes into a BitArray. - BitArray bitArray; - if (utf8BOM) - { - // convert to bit array, leaving 24 bits for the UTF-8 preamble - bitArray = ToBitArray(codeBytes, 24); - // write UTF8 preamble (EF BB BF) to the BitArray - DecToBin(0xEF, 8, bitArray, 0); - DecToBin(0xBB, 8, bitArray, 8); - DecToBin(0xBF, 8, bitArray, 16); - } - else - { - bitArray = ToBitArray(codeBytes); - } + // Convert the array of bytes into a BitArray. + BitArray bitArray; + if (utf8BOM) + { + // convert to bit array, leaving 24 bits for the UTF-8 preamble + bitArray = ToBitArray(codeBytes, 24); + // write UTF8 preamble (EF BB BF) to the BitArray + DecToBin(0xEF, 8, bitArray, 0); + DecToBin(0xBB, 8, bitArray, 8); + DecToBin(0xBF, 8, bitArray, 16); + } + else + { + bitArray = ToBitArray(codeBytes); + } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1 - if (bufferFromPool != null) - ArrayPool.Shared.Return(bufferFromPool); + if (bufferFromPool != null) + ArrayPool.Shared.Return(bufferFromPool); #endif - return bitArray; - } + return bitArray; + } - /// - /// Converts an array of bytes into a BitArray, considering the proper bit order within each byte. - /// Unlike the constructor of BitArray, this function preserves the MSB-to-LSB order within each byte. - /// - /// The byte array to convert into a BitArray. - /// The number of leading zeros to prepend to the resulting BitArray. - /// A BitArray representing the bits of the input byteArray, with optional leading zeros. - private static BitArray ToBitArray( + /// + /// Converts an array of bytes into a BitArray, considering the proper bit order within each byte. + /// Unlike the constructor of BitArray, this function preserves the MSB-to-LSB order within each byte. + /// + /// The byte array to convert into a BitArray. + /// The number of leading zeros to prepend to the resulting BitArray. + /// A BitArray representing the bits of the input byteArray, with optional leading zeros. + private static BitArray ToBitArray( #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1 - ReadOnlySpan byteArray, // byte[] has an implicit cast to ReadOnlySpan + ReadOnlySpan byteArray, // byte[] has an implicit cast to ReadOnlySpan #else - byte[] byteArray, + byte[] byteArray, #endif - int prefixZeros = 0) + int prefixZeros = 0) + { + // Calculate the total number of bits in the resulting BitArray including the prefix zeros. + var bitArray = new BitArray((int)((uint)byteArray.Length * 8) + prefixZeros); + for (var i = 0; i < byteArray.Length; i++) { - // Calculate the total number of bits in the resulting BitArray including the prefix zeros. - var bitArray = new BitArray((int)((uint)byteArray.Length * 8) + prefixZeros); - for (var i = 0; i < byteArray.Length; i++) + var byteVal = byteArray[i]; + for (var j = 0; j < 8; j++) { - var byteVal = byteArray[i]; - for (var j = 0; j < 8; j++) - { - // Set each bit in the BitArray based on the corresponding bit in the byte array. - // It shifts bits within the byte to align with the MSB-to-LSB order. - bitArray[(int)((uint)i * 8) + j + prefixZeros] = (byteVal & (1 << (7 - j))) != 0; - } + // Set each bit in the BitArray based on the corresponding bit in the byte array. + // It shifts bits within the byte to align with the MSB-to-LSB order. + bitArray[(int)((uint)i * 8) + j + prefixZeros] = (byteVal & (1 << (7 - j))) != 0; } - return bitArray; } + return bitArray; + } - /// - /// Performs a bitwise XOR operation between two polynomials, commonly used in QR code error correction coding. - /// - /// The resultant polynomial after performing the XOR operation. - private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) + /// + /// Performs a bitwise XOR operation between two polynomials, commonly used in QR code error correction coding. + /// + /// The resultant polynomial after performing the XOR operation. + private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) + { + // Determine the larger of the two polynomials to guide the XOR operation. + var resultPolynom = new Polynom(Math.Max(messagePolynom.Count, resPolynom.Count) - 1); + Polynom longPoly, shortPoly; + if (messagePolynom.Count >= resPolynom.Count) { - // Determine the larger of the two polynomials to guide the XOR operation. - var resultPolynom = new Polynom(Math.Max(messagePolynom.Count, resPolynom.Count) - 1); - Polynom longPoly, shortPoly; - if (messagePolynom.Count >= resPolynom.Count) - { - longPoly = messagePolynom; - shortPoly = resPolynom; - } - else - { - longPoly = resPolynom; - shortPoly = messagePolynom; - } + longPoly = messagePolynom; + shortPoly = resPolynom; + } + else + { + longPoly = resPolynom; + shortPoly = messagePolynom; + } - // XOR the coefficients of the two polynomials. - for (var i = 1; i < longPoly.Count; i++) - { - var polItemRes = new PolynomItem( - longPoly[i].Coefficient ^ - (shortPoly.Count > i ? shortPoly[i].Coefficient : 0), - messagePolynom[0].Exponent - i - ); - resultPolynom.Add(polItemRes); - } + // XOR the coefficients of the two polynomials. + for (var i = 1; i < longPoly.Count; i++) + { + var polItemRes = new PolynomItem( + longPoly[i].Coefficient ^ + (shortPoly.Count > i ? shortPoly[i].Coefficient : 0), + messagePolynom[0].Exponent - i + ); + resultPolynom.Add(polItemRes); + } - return resultPolynom; + return resultPolynom; + } + + /// + /// Multiplies a generator polynomial by a leading term polynomial, reducing the result by a specified lower exponent, + /// used in constructing QR code error correction codewords. + /// + private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) + { + var resultPolynom = new Polynom(genPolynom.Count); + foreach (var polItemBase in genPolynom) + { + var polItemRes = new PolynomItem( + + (polItemBase.Coefficient + leadTerm.Coefficient) % 255, + polItemBase.Exponent - lowerExponentBy + ); + resultPolynom.Add(polItemRes); } + return resultPolynom; + } - /// - /// Multiplies a generator polynomial by a leading term polynomial, reducing the result by a specified lower exponent, - /// used in constructing QR code error correction codewords. - /// - private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) + /// + /// Multiplies two polynomials, treating coefficients as exponents of a primitive element (alpha), which is common in error correction algorithms such as Reed-Solomon. + /// + /// The first polynomial to multiply. + /// The second polynomial to multiply. + /// A new polynomial which is the result of the multiplication of the two input polynomials. + private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) + { + // Initialize a new polynomial with a size based on the product of the sizes of the two input polynomials. + var resultPolynom = new Polynom(polynomMultiplier.Count * polynomBase.Count); + + // Multiply each term of the first polynomial by each term of the second polynomial. + foreach (var polItemBase in polynomMultiplier) { - var resultPolynom = new Polynom(genPolynom.Count); - foreach (var polItemBase in genPolynom) + foreach (var polItemMulti in polynomBase) { - var polItemRes = new PolynomItem( - - (polItemBase.Coefficient + leadTerm.Coefficient) % 255, - polItemBase.Exponent - lowerExponentBy + // Create a new polynomial term with the coefficients added (as exponents) and exponents summed. + var polItemRes = new PolynomItem + ( + ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), + (polItemBase.Exponent + polItemMulti.Exponent) ); resultPolynom.Add(polItemRes); } - return resultPolynom; } - /// - /// Multiplies two polynomials, treating coefficients as exponents of a primitive element (alpha), which is common in error correction algorithms such as Reed-Solomon. - /// - /// The first polynomial to multiply. - /// The second polynomial to multiply. - /// A new polynomial which is the result of the multiplication of the two input polynomials. - private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) + // Identify and merge terms with the same exponent. + var toGlue = GetNotUniqueExponents(resultPolynom); + var gluedPolynoms = new PolynomItem[toGlue.Length]; + var gluedPolynomsIndex = 0; + foreach (var exponent in toGlue) { - // Initialize a new polynomial with a size based on the product of the sizes of the two input polynomials. - var resultPolynom = new Polynom(polynomMultiplier.Count * polynomBase.Count); - - // Multiply each term of the first polynomial by each term of the second polynomial. - foreach (var polItemBase in polynomMultiplier) + var coefficient = 0; + foreach (var polynomOld in resultPolynom) { - foreach (var polItemMulti in polynomBase) - { - // Create a new polynomial term with the coefficients added (as exponents) and exponents summed. - var polItemRes = new PolynomItem - ( - ShrinkAlphaExp(polItemBase.Coefficient + polItemMulti.Coefficient), - (polItemBase.Exponent + polItemMulti.Exponent) - ); - resultPolynom.Add(polItemRes); - } + if (polynomOld.Exponent == exponent) + coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); } - // Identify and merge terms with the same exponent. - var toGlue = GetNotUniqueExponents(resultPolynom); - var gluedPolynoms = new PolynomItem[toGlue.Length]; - var gluedPolynomsIndex = 0; - foreach (var exponent in toGlue) - { - var coefficient = 0; - foreach (var polynomOld in resultPolynom) - { - if (polynomOld.Exponent == exponent) - coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); - } - - // Fix the polynomial terms by recalculating the coefficients based on XORed results. - var polynomFixed = new PolynomItem(GetAlphaExpFromIntVal(coefficient), exponent); - gluedPolynoms[gluedPolynomsIndex++] = polynomFixed; - } + // Fix the polynomial terms by recalculating the coefficients based on XORed results. + var polynomFixed = new PolynomItem(GetAlphaExpFromIntVal(coefficient), exponent); + gluedPolynoms[gluedPolynomsIndex++] = polynomFixed; + } - // Remove duplicated exponents and add the corrected ones back. - for (int i = resultPolynom.Count - 1; i >= 0; i--) - if (toGlue.Contains(resultPolynom[i].Exponent)) - resultPolynom.RemoveAt(i); - foreach (var polynom in gluedPolynoms) - resultPolynom.Add(polynom); + // Remove duplicated exponents and add the corrected ones back. + for (int i = resultPolynom.Count - 1; i >= 0; i--) + if (toGlue.Contains(resultPolynom[i].Exponent)) + resultPolynom.RemoveAt(i); + foreach (var polynom in gluedPolynoms) + resultPolynom.Add(polynom); - // Sort the polynomial terms by exponent in descending order. - resultPolynom.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); - return resultPolynom; + // Sort the polynomial terms by exponent in descending order. + resultPolynom.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); + return resultPolynom; - // Auxiliary function to identify exponents that appear more than once in the polynomial. - int[] GetNotUniqueExponents(Polynom list) + // Auxiliary function to identify exponents that appear more than once in the polynomial. + int[] GetNotUniqueExponents(Polynom list) + { + var dic = new Dictionary(list.Count); + foreach (var row in list) { - var dic = new Dictionary(list.Count); - foreach (var row in list) - { #if NETCOREAPP - if (dic.TryAdd(row.Exponent, false)) - dic[row.Exponent] = true; + if (dic.TryAdd(row.Exponent, false)) + dic[row.Exponent] = true; #else - if (!dic.ContainsKey(row.Exponent)) - dic.Add(row.Exponent, false); - else - dic[row.Exponent] = true; + if (!dic.ContainsKey(row.Exponent)) + dic.Add(row.Exponent, false); + else + dic[row.Exponent] = true; #endif - } - - // Collect all exponents that appeared more than once. - int count = 0; - foreach (var row in dic) - { - if (row.Value) - count++; - } + } - var result = new int[count]; - int i = 0; - foreach (var row in dic) - { - if (row.Value) - result[i++] = row.Key; - } + // Collect all exponents that appeared more than once. + int count = 0; + foreach (var row in dic) + { + if (row.Value) + count++; + } - return result; + var result = new int[count]; + int i = 0; + foreach (var row in dic) + { + if (row.Value) + result[i++] = row.Key; } - } - /// - /// Retrieves the integer value from the Galois field that corresponds to a given exponent. - /// This is used in Reed-Solomon and other error correction calculations involving Galois fields. - /// - private static int GetIntValFromAlphaExp(int exp) - { - return galoisFieldByExponentAlpha[exp]; + return result; } + } - /// - /// Retrieves the exponent from the Galois field that corresponds to a given integer value. - /// Throws an exception if the integer value is zero, as zero does not have a logarithmic representation in the field. - /// - private static int GetAlphaExpFromIntVal(int intVal) - { - if (intVal == 0) - ThrowIntValOutOfRangeException(); // Zero is not valid as it does not have an exponent representation. - return galoisFieldByIntegerValue[intVal]; + /// + /// Retrieves the integer value from the Galois field that corresponds to a given exponent. + /// This is used in Reed-Solomon and other error correction calculations involving Galois fields. + /// + private static int GetIntValFromAlphaExp(int exp) + => _galoisFieldByExponentAlpha[exp]; - void ThrowIntValOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(intVal), "The provided integer value is out of range, as zero is not representable."); - } + /// + /// Retrieves the exponent from the Galois field that corresponds to a given integer value. + /// Throws an exception if the integer value is zero, as zero does not have a logarithmic representation in the field. + /// + private static int GetAlphaExpFromIntVal(int intVal) + { + if (intVal == 0) + ThrowIntValOutOfRangeException(); // Zero is not valid as it does not have an exponent representation. + return _galoisFieldByIntegerValue[intVal]; - /// - /// Normalizes a Galois field exponent to ensure it remains within the bounds of the field's size. - /// This is particularly necessary when performing multiplications in the field which can result in exponents exceeding the field's maximum. - /// - private static int ShrinkAlphaExp(int alphaExp) - { - return (int)((alphaExp % 256) + Math.Floor((double)(alphaExp / 256))); - } + void ThrowIntValOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(intVal), "The provided integer value is out of range, as zero is not representable."); + } - /// - /// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. - /// This includes digits 0-9, uppercase letters A-Z, and some special characters. - /// - /// A dictionary mapping each supported alphanumeric character to its corresponding value. - private static Dictionary CreateAlphanumEncDict() - { - var localAlphanumEncDict = new Dictionary(45); - for (int i = 0; i < 10; i++) - localAlphanumEncDict.Add($"{i}"[0], i); - // Add uppercase alphabetic characters. - for (char c = 'A'; c <= 'Z'; c++) - localAlphanumEncDict.Add(c, localAlphanumEncDict.Count); - // Add special characters from a predefined table. - for (int i = 0; i < alphanumEncTable.Length; i++) - localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count); - return localAlphanumEncDict; - } + /// + /// Normalizes a Galois field exponent to ensure it remains within the bounds of the field's size. + /// This is particularly necessary when performing multiplications in the field which can result in exponents exceeding the field's maximum. + /// + private static int ShrinkAlphaExp(int alphaExp) + => (int)((alphaExp % 256) + Math.Floor((double)(alphaExp / 256))); - /// - /// Creates a lookup table mapping QR code versions to their corresponding alignment patterns. - /// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured. - /// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code. - /// - /// A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version. - private static Dictionary CreateAlignmentPatternTable() - { - var localAlignmentPatternTable = new Dictionary(40); + /// + /// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. + /// This includes digits 0-9, uppercase letters A-Z, and some special characters. + /// + /// A dictionary mapping each supported alphanumeric character to its corresponding value. + private static Dictionary CreateAlphanumEncDict() + { + var localAlphanumEncDict = new Dictionary(45); + for (int i = 0; i < 10; i++) + localAlphanumEncDict.Add($"{i}"[0], i); + // Add uppercase alphabetic characters. + for (char c = 'A'; c <= 'Z'; c++) + localAlphanumEncDict.Add(c, localAlphanumEncDict.Count); + // Add special characters from a predefined table. + for (int i = 0; i < _alphanumEncTable.Length; i++) + localAlphanumEncDict.Add(_alphanumEncTable[i], localAlphanumEncDict.Count); + return localAlphanumEncDict; + } - for (var i = 0; i < (7 * 40); i = i + 7) + /// + /// Creates a lookup table mapping QR code versions to their corresponding alignment patterns. + /// Alignment patterns are used in QR codes to help scanners accurately read the code at high speeds and when partially obscured. + /// This table provides the necessary patterns based on the QR code version which dictates the size and complexity of the QR code. + /// + /// A dictionary where keys are QR code version numbers and values are AlignmentPattern structures detailing the positions of alignment patterns for each version. + private static Dictionary CreateAlignmentPatternTable() + { + var localAlignmentPatternTable = new Dictionary(40); + + for (var i = 0; i < (7 * 40); i += 7) + { + var points = new List(50); + for (var x = 0; x < 7; x++) { - var points = new List(50); - for (var x = 0; x < 7; x++) + if (_alignmentPatternBaseValues[i + x] != 0) { - if (alignmentPatternBaseValues[i + x] != 0) + for (var y = 0; y < 7; y++) { - for (var y = 0; y < 7; y++) + if (_alignmentPatternBaseValues[i + y] != 0) { - if (alignmentPatternBaseValues[i + y] != 0) - { - var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2); - if (!points.Contains(p)) - points.Add(p); - } + var p = new Point(_alignmentPatternBaseValues[i + x] - 2, _alignmentPatternBaseValues[i + y] - 2); + if (!points.Contains(p)) + points.Add(p); } } } - - var version = (i + 7) / 7; - localAlignmentPatternTable.Add(version, new AlignmentPattern() - { - Version = version, - PatternPositions = points - } - ); } - return localAlignmentPatternTable; - } - /// - /// Generates a table containing the error correction capacities and data codeword information for different QR code versions and error correction levels. - /// This table is essential for determining how much data can be encoded in a QR code of a specific version and ECC level, - /// as well as how robust the QR code will be against distortions or obstructions. - /// - /// A list of ECCInfo structures, each representing the ECC data and capacities for different combinations of QR code versions and ECC levels. - private static List CreateCapacityECCTable() - { - var localCapacityECCTable = new List(160); - for (var i = 0; i < (4 * 6 * 40); i = i + (4 * 6)) + var version = (i + 7) / 7; + localAlignmentPatternTable.Add(version, new AlignmentPattern() { - localCapacityECCTable.AddRange( - new[] - { - new ECCInfo( - (i+24) / 24, - ECCLevel.L, - capacityECCBaseValues[i], - capacityECCBaseValues[i+1], - capacityECCBaseValues[i+2], - capacityECCBaseValues[i+3], - capacityECCBaseValues[i+4], - capacityECCBaseValues[i+5]), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.M, - totalDataCodewords: capacityECCBaseValues[i+6], - eccPerBlock: capacityECCBaseValues[i+7], - blocksInGroup1: capacityECCBaseValues[i+8], - codewordsInGroup1: capacityECCBaseValues[i+9], - blocksInGroup2: capacityECCBaseValues[i+10], - codewordsInGroup2: capacityECCBaseValues[i+11] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.Q, - totalDataCodewords: capacityECCBaseValues[i+12], - eccPerBlock: capacityECCBaseValues[i+13], - blocksInGroup1: capacityECCBaseValues[i+14], - codewordsInGroup1: capacityECCBaseValues[i+15], - blocksInGroup2: capacityECCBaseValues[i+16], - codewordsInGroup2: capacityECCBaseValues[i+17] - ), - new ECCInfo - ( - version: (i + 24) / 24, - errorCorrectionLevel: ECCLevel.H, - totalDataCodewords: capacityECCBaseValues[i+18], - eccPerBlock: capacityECCBaseValues[i+19], - blocksInGroup1: capacityECCBaseValues[i+20], - codewordsInGroup1: capacityECCBaseValues[i+21], - blocksInGroup2: capacityECCBaseValues[i+22], - codewordsInGroup2: capacityECCBaseValues[i+23] - ) - }); + Version = version, + PatternPositions = points } - return localCapacityECCTable; + ); } + return localAlignmentPatternTable; + } - /// - /// Generates a list containing detailed capacity information for various versions of QR codes. - /// This table includes capacities for different encoding modes (numeric, alphanumeric, byte, etc.) under each error correction level. - /// The capacity table is crucial for QR code generation, as it determines how much data each QR code version can store depending on the encoding mode and error correction level used. - /// - private static List CreateCapacityTable() + /// + /// Generates a table containing the error correction capacities and data codeword information for different QR code versions and error correction levels. + /// This table is essential for determining how much data can be encoded in a QR code of a specific version and ECC level, + /// as well as how robust the QR code will be against distortions or obstructions. + /// + /// A list of ECCInfo structures, each representing the ECC data and capacities for different combinations of QR code versions and ECC levels. + private static List CreateCapacityECCTable() + { + var localCapacityECCTable = new List(160); + for (var i = 0; i < (4 * 6 * 40); i += (4 * 6)) { - var localCapacityTable = new List(40); - for (var i = 0; i < (16 * 40); i = i + 16) + localCapacityECCTable.AddRange( + new[] { - localCapacityTable.Add(new VersionInfo( - - (i + 16) / 16, - new List(4) - { - new VersionInfoDetails( - ECCLevel.L, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+1] }, - { EncodingMode.Byte, capacityBaseValues[i+2] }, - { EncodingMode.Kanji, capacityBaseValues[i+3] }, - } - ), - new VersionInfoDetails( - ECCLevel.M, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+4] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+5] }, - { EncodingMode.Byte, capacityBaseValues[i+6] }, - { EncodingMode.Kanji, capacityBaseValues[i+7] }, - } - ), - new VersionInfoDetails( - ECCLevel.Q, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+8] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+9] }, - { EncodingMode.Byte, capacityBaseValues[i+10] }, - { EncodingMode.Kanji, capacityBaseValues[i+11] }, - } - ), - new VersionInfoDetails( - ECCLevel.H, - new Dictionary(){ - { EncodingMode.Numeric, capacityBaseValues[i+12] }, - { EncodingMode.Alphanumeric, capacityBaseValues[i+13] }, - { EncodingMode.Byte, capacityBaseValues[i+14] }, - { EncodingMode.Kanji, capacityBaseValues[i+15] }, - } - ) - } - )); - } - return localCapacityTable; + new ECCInfo( + (i+24) / 24, + ECCLevel.L, + _capacityECCBaseValues[i], + _capacityECCBaseValues[i+1], + _capacityECCBaseValues[i+2], + _capacityECCBaseValues[i+3], + _capacityECCBaseValues[i+4], + _capacityECCBaseValues[i+5]), + new ECCInfo + ( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.M, + totalDataCodewords: _capacityECCBaseValues[i+6], + eccPerBlock: _capacityECCBaseValues[i+7], + blocksInGroup1: _capacityECCBaseValues[i+8], + codewordsInGroup1: _capacityECCBaseValues[i+9], + blocksInGroup2: _capacityECCBaseValues[i+10], + codewordsInGroup2: _capacityECCBaseValues[i+11] + ), + new ECCInfo + ( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.Q, + totalDataCodewords: _capacityECCBaseValues[i+12], + eccPerBlock: _capacityECCBaseValues[i+13], + blocksInGroup1: _capacityECCBaseValues[i+14], + codewordsInGroup1: _capacityECCBaseValues[i+15], + blocksInGroup2: _capacityECCBaseValues[i+16], + codewordsInGroup2: _capacityECCBaseValues[i+17] + ), + new ECCInfo + ( + version: (i + 24) / 24, + errorCorrectionLevel: ECCLevel.H, + totalDataCodewords: _capacityECCBaseValues[i+18], + eccPerBlock: _capacityECCBaseValues[i+19], + blocksInGroup1: _capacityECCBaseValues[i+20], + codewordsInGroup1: _capacityECCBaseValues[i+21], + blocksInGroup2: _capacityECCBaseValues[i+22], + codewordsInGroup2: _capacityECCBaseValues[i+23] + ) + }); } + return localCapacityECCTable; + } - /// - public void Dispose() + /// + /// Generates a list containing detailed capacity information for various versions of QR codes. + /// This table includes capacities for different encoding modes (numeric, alphanumeric, byte, etc.) under each error correction level. + /// The capacity table is crucial for QR code generation, as it determines how much data each QR code version can store depending on the encoding mode and error correction level used. + /// + private static List CreateCapacityTable() + { + var localCapacityTable = new List(40); + for (var i = 0; i < (16 * 40); i += 16) { - // left for back-compat + localCapacityTable.Add(new VersionInfo( + + (i + 16) / 16, + new List(4) + { + new VersionInfoDetails( + ECCLevel.L, + new Dictionary(){ + { EncodingMode.Numeric, _capacityBaseValues[i] }, + { EncodingMode.Alphanumeric, _capacityBaseValues[i+1] }, + { EncodingMode.Byte, _capacityBaseValues[i+2] }, + { EncodingMode.Kanji, _capacityBaseValues[i+3] }, + } + ), + new VersionInfoDetails( + ECCLevel.M, + new Dictionary(){ + { EncodingMode.Numeric, _capacityBaseValues[i+4] }, + { EncodingMode.Alphanumeric, _capacityBaseValues[i+5] }, + { EncodingMode.Byte, _capacityBaseValues[i+6] }, + { EncodingMode.Kanji, _capacityBaseValues[i+7] }, + } + ), + new VersionInfoDetails( + ECCLevel.Q, + new Dictionary(){ + { EncodingMode.Numeric, _capacityBaseValues[i+8] }, + { EncodingMode.Alphanumeric, _capacityBaseValues[i+9] }, + { EncodingMode.Byte, _capacityBaseValues[i+10] }, + { EncodingMode.Kanji, _capacityBaseValues[i+11] }, + } + ), + new VersionInfoDetails( + ECCLevel.H, + new Dictionary(){ + { EncodingMode.Numeric, _capacityBaseValues[i+12] }, + { EncodingMode.Alphanumeric, _capacityBaseValues[i+13] }, + { EncodingMode.Byte, _capacityBaseValues[i+14] }, + { EncodingMode.Kanji, _capacityBaseValues[i+15] }, + } + ) + } + )); } + return localCapacityTable; + } + + /// + public void Dispose() + { + // left for back-compat } } diff --git a/QRCoder/QRCodeGenerator/AlignmentPattern.cs b/QRCoder/QRCodeGenerator/AlignmentPattern.cs index f7d85152..77a707d7 100644 --- a/QRCoder/QRCodeGenerator/AlignmentPattern.cs +++ b/QRCoder/QRCodeGenerator/AlignmentPattern.cs @@ -1,25 +1,24 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents the alignment pattern used in QR codes, which helps ensure the code remains readable even if it is somewhat distorted. + /// Each QR code version has its own specific alignment pattern locations which this struct encapsulates. + /// + private struct AlignmentPattern { /// - /// Represents the alignment pattern used in QR codes, which helps ensure the code remains readable even if it is somewhat distorted. - /// Each QR code version has its own specific alignment pattern locations which this struct encapsulates. + /// The version of the QR code. Higher versions have more complex and numerous alignment patterns. /// - private struct AlignmentPattern - { - /// - /// The version of the QR code. Higher versions have more complex and numerous alignment patterns. - /// - public int Version; + public int Version; - /// - /// A list of points where alignment patterns are located within the QR code matrix. - /// Each point represents the center of an alignment pattern. - /// - public List PatternPositions; - } + /// + /// A list of points where alignment patterns are located within the QR code matrix. + /// Each point represents the center of an alignment pattern. + /// + public List PatternPositions; } } diff --git a/QRCoder/QRCodeGenerator/CodewordBlock.cs b/QRCoder/QRCodeGenerator/CodewordBlock.cs index 2d53efe4..6960219e 100644 --- a/QRCoder/QRCodeGenerator/CodewordBlock.cs +++ b/QRCoder/QRCodeGenerator/CodewordBlock.cs @@ -1,42 +1,39 @@ -using System.Collections; +namespace QRCoder; -namespace QRCoder +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents a block of codewords in a QR code. QR codes are divided into several blocks for error correction purposes. + /// Each block contains a series of data codewords followed by error correction codewords. + /// + private struct CodewordBlock { /// - /// Represents a block of codewords in a QR code. QR codes are divided into several blocks for error correction purposes. - /// Each block contains a series of data codewords followed by error correction codewords. + /// Initializes a new instance of the CodewordBlock struct with specified arrays of code words and error correction (ECC) words. /// - private struct CodewordBlock + /// The offset of the data codewords within the main BitArray. Data codewords carry the actual information. + /// The length in bits of the data codewords within the main BitArray. + /// The array of error correction codewords for this block. These codewords help recover the data if the QR code is damaged. + public CodewordBlock(int codeWordsOffset, int codeWordsLength, byte[] eccWords) { - /// - /// Initializes a new instance of the CodewordBlock struct with specified arrays of code words and error correction (ECC) words. - /// - /// The offset of the data codewords within the main BitArray. Data codewords carry the actual information. - /// The length in bits of the data codewords within the main BitArray. - /// The array of error correction codewords for this block. These codewords help recover the data if the QR code is damaged. - public CodewordBlock(int codeWordsOffset, int codeWordsLength, byte[] eccWords) - { - this.CodeWordsOffset = codeWordsOffset; - this.CodeWordsLength = codeWordsLength; - this.ECCWords = eccWords; - } + CodeWordsOffset = codeWordsOffset; + CodeWordsLength = codeWordsLength; + ECCWords = eccWords; + } - /// - /// Gets the offset of the data codewords in the BitArray. - /// - public int CodeWordsOffset { get; } + /// + /// Gets the offset of the data codewords in the BitArray. + /// + public int CodeWordsOffset { get; } - /// - /// Gets the length of the data codewords in the BitArray. - /// - public int CodeWordsLength { get; } + /// + /// Gets the length of the data codewords in the BitArray. + /// + public int CodeWordsLength { get; } - /// - /// Gets the error correction codewords associated with this block. - /// - public byte[] ECCWords { get; } - } + /// + /// Gets the error correction codewords associated with this block. + /// + public byte[] ECCWords { get; } } } diff --git a/QRCoder/QRCodeGenerator/ECCInfo.cs b/QRCoder/QRCodeGenerator/ECCInfo.cs index fcf0b427..919288ca 100644 --- a/QRCoder/QRCodeGenerator/ECCInfo.cs +++ b/QRCoder/QRCodeGenerator/ECCInfo.cs @@ -1,75 +1,74 @@ -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents the error correction coding (ECC) information for a specific version and error correction level of a QR code. + /// + private struct ECCInfo { /// - /// Represents the error correction coding (ECC) information for a specific version and error correction level of a QR code. + /// Initializes a new instance of the ECCInfo struct with specified properties. /// - private struct ECCInfo + /// The version number of the QR code. + /// The error correction level used in the QR code. + /// The total number of data codewords for this version and error correction level. + /// The number of error correction codewords per block. + /// The number of blocks in group 1. + /// The number of codewords in each block of group 1. + /// The number of blocks in group 2, if any. + /// The number of codewords in each block of group 2, if any. + public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodewords, int eccPerBlock, int blocksInGroup1, + int codewordsInGroup1, int blocksInGroup2, int codewordsInGroup2) { - /// - /// Initializes a new instance of the ECCInfo struct with specified properties. - /// - /// The version number of the QR code. - /// The error correction level used in the QR code. - /// The total number of data codewords for this version and error correction level. - /// The number of error correction codewords per block. - /// The number of blocks in group 1. - /// The number of codewords in each block of group 1. - /// The number of blocks in group 2, if any. - /// The number of codewords in each block of group 2, if any. - public ECCInfo(int version, ECCLevel errorCorrectionLevel, int totalDataCodewords, int eccPerBlock, int blocksInGroup1, - int codewordsInGroup1, int blocksInGroup2, int codewordsInGroup2) - { - this.Version = version; - this.ErrorCorrectionLevel = errorCorrectionLevel; - this.TotalDataCodewords = totalDataCodewords; - this.ECCPerBlock = eccPerBlock; - this.BlocksInGroup1 = blocksInGroup1; - this.CodewordsInGroup1 = codewordsInGroup1; - this.BlocksInGroup2 = blocksInGroup2; - this.CodewordsInGroup2 = codewordsInGroup2; - } + Version = version; + ErrorCorrectionLevel = errorCorrectionLevel; + TotalDataCodewords = totalDataCodewords; + ECCPerBlock = eccPerBlock; + BlocksInGroup1 = blocksInGroup1; + CodewordsInGroup1 = codewordsInGroup1; + BlocksInGroup2 = blocksInGroup2; + CodewordsInGroup2 = codewordsInGroup2; + } - /// - /// Gets the version number of the QR code. - /// - public int Version { get; } + /// + /// Gets the version number of the QR code. + /// + public int Version { get; } - /// - /// Gets the error correction level of the QR code. - /// - public ECCLevel ErrorCorrectionLevel { get; } + /// + /// Gets the error correction level of the QR code. + /// + public ECCLevel ErrorCorrectionLevel { get; } - /// - /// Gets the total number of data codewords for this version and error correction level. - /// - public int TotalDataCodewords { get; } + /// + /// Gets the total number of data codewords for this version and error correction level. + /// + public int TotalDataCodewords { get; } - /// - /// Gets the number of error correction codewords per block. - /// - public int ECCPerBlock { get; } + /// + /// Gets the number of error correction codewords per block. + /// + public int ECCPerBlock { get; } - /// - /// Gets the number of blocks in group 1. - /// - public int BlocksInGroup1 { get; } + /// + /// Gets the number of blocks in group 1. + /// + public int BlocksInGroup1 { get; } - /// - /// Gets the number of codewords in each block of group 1. - /// - public int CodewordsInGroup1 { get; } + /// + /// Gets the number of codewords in each block of group 1. + /// + public int CodewordsInGroup1 { get; } - /// - /// Gets the number of blocks in group 2, if any. - /// - public int BlocksInGroup2 { get; } + /// + /// Gets the number of blocks in group 2, if any. + /// + public int BlocksInGroup2 { get; } - /// - /// Gets the number of codewords in each block of group 2, if any. - /// - public int CodewordsInGroup2 { get; } - } + /// + /// Gets the number of codewords in each block of group 2, if any. + /// + public int CodewordsInGroup2 { get; } } } diff --git a/QRCoder/QRCodeGenerator/ECCLevel.cs b/QRCoder/QRCodeGenerator/ECCLevel.cs index d8a0ad57..341b81eb 100644 --- a/QRCoder/QRCodeGenerator/ECCLevel.cs +++ b/QRCoder/QRCodeGenerator/ECCLevel.cs @@ -1,42 +1,41 @@ -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Defines the levels of error correction available in QR codes. + /// Each level specifies the proportion of data that can be recovered if the QR code is partially obscured or damaged. + /// + public enum ECCLevel { /// - /// Defines the levels of error correction available in QR codes. - /// Each level specifies the proportion of data that can be recovered if the QR code is partially obscured or damaged. + /// Default error correction level, which will select Level M (Medium) unless otherwise specified by the payload. + /// Level M allows approximately 15% of data to be recovered, offering a balance between data capacity and error recovery. /// - public enum ECCLevel - { - /// - /// Default error correction level, which will select Level M (Medium) unless otherwise specified by the payload. - /// Level M allows approximately 15% of data to be recovered, offering a balance between data capacity and error recovery. - /// - Default = -1, + Default = -1, - /// - /// Level L: Low error correction (approximately 7% of data can be recovered). - /// This level allows the highest data density. - /// - L = 0, + /// + /// Level L: Low error correction (approximately 7% of data can be recovered). + /// This level allows the highest data density. + /// + L = 0, - /// - /// Level M: Medium error correction (approximately 15% of data can be recovered). - /// Offers a balance between data capacity and error recovery. - /// - M = 1, + /// + /// Level M: Medium error correction (approximately 15% of data can be recovered). + /// Offers a balance between data capacity and error recovery. + /// + M = 1, - /// - /// Level Q: Quartile error correction (approximately 25% of data can be recovered). - /// More robust error correction at the cost of reduced data capacity. - /// - Q = 2, + /// + /// Level Q: Quartile error correction (approximately 25% of data can be recovered). + /// More robust error correction at the cost of reduced data capacity. + /// + Q = 2, - /// - /// Level H: High error correction (approximately 30% of data can be recovered). - /// Provides the highest level of error recovery, ideal for environments with high risk of data loss. - /// - H = 3 - } + /// + /// Level H: High error correction (approximately 30% of data can be recovered). + /// Provides the highest level of error recovery, ideal for environments with high risk of data loss. + /// + H = 3 } } diff --git a/QRCoder/QRCodeGenerator/EciMode.cs b/QRCoder/QRCodeGenerator/EciMode.cs index 04a6868c..4cf253be 100644 --- a/QRCoder/QRCodeGenerator/EciMode.cs +++ b/QRCoder/QRCodeGenerator/EciMode.cs @@ -1,36 +1,35 @@ -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Enumerates the Extended Channel Interpretation (ECI) modes used in QR codes to handle different character encoding standards. + /// ECI mode allows QR codes to efficiently encode data using character sets other than the default ISO-8859-1. + /// + public enum EciMode { /// - /// Enumerates the Extended Channel Interpretation (ECI) modes used in QR codes to handle different character encoding standards. - /// ECI mode allows QR codes to efficiently encode data using character sets other than the default ISO-8859-1. + /// Default encoding mode (typically ISO-8859-1). Used when no ECI mode is explicitly specified. + /// This mode is assumed in basic QR codes where no extended character sets are needed. /// - public enum EciMode - { - /// - /// Default encoding mode (typically ISO-8859-1). Used when no ECI mode is explicitly specified. - /// This mode is assumed in basic QR codes where no extended character sets are needed. - /// - Default = 0, + Default = 0, - /// - /// Specifies the use of the ISO-8859-1 character set, covering most Western European languages. - /// This mode explicitly sets the encoding to ISO-8859-1, which includes characters used in languages such as English, French, German, and Spanish. - /// - Iso8859_1 = 3, + /// + /// Specifies the use of the ISO-8859-1 character set, covering most Western European languages. + /// This mode explicitly sets the encoding to ISO-8859-1, which includes characters used in languages such as English, French, German, and Spanish. + /// + Iso8859_1 = 3, - /// - /// Specifies the use of the ISO-8859-2 character set, which is primarily used for Central and Eastern European languages. - /// This includes characters used in languages such as Polish, Czech, Slovak, Hungarian, and Romanian. - /// - Iso8859_2 = 4, + /// + /// Specifies the use of the ISO-8859-2 character set, which is primarily used for Central and Eastern European languages. + /// This includes characters used in languages such as Polish, Czech, Slovak, Hungarian, and Romanian. + /// + Iso8859_2 = 4, - /// - /// Specifies the use of UTF-8 encoding. - /// UTF-8 can encode any Unicode character and is useful for QR codes that need to support multi-language content. - /// - Utf8 = 26 - } + /// + /// Specifies the use of UTF-8 encoding. + /// UTF-8 can encode any Unicode character and is useful for QR codes that need to support multi-language content. + /// + Utf8 = 26 } } diff --git a/QRCoder/QRCodeGenerator/EncodingMode.cs b/QRCoder/QRCodeGenerator/EncodingMode.cs index 33e2a6ed..f200573e 100644 --- a/QRCoder/QRCodeGenerator/EncodingMode.cs +++ b/QRCoder/QRCodeGenerator/EncodingMode.cs @@ -1,43 +1,40 @@ -using System; +namespace QRCoder; -namespace QRCoder +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Specifies the encoding modes for the characters in a QR code. + /// + private enum EncodingMode { /// - /// Specifies the encoding modes for the characters in a QR code. + /// Numeric encoding mode, which is used to encode numeric data (digits 0-9). + /// Three characters are encoded into 10 bits. /// - private enum EncodingMode - { - /// - /// Numeric encoding mode, which is used to encode numeric data (digits 0-9). - /// Three characters are encoded into 10 bits. - /// - Numeric = 1, + Numeric = 1, - /// - /// Alphanumeric encoding mode, which is used to encode alphanumeric characters (0-9, A-Z, space, and some punctuation). - /// Two characters are encoded into 11 bits. - /// - Alphanumeric = 2, + /// + /// Alphanumeric encoding mode, which is used to encode alphanumeric characters (0-9, A-Z, space, and some punctuation). + /// Two characters are encoded into 11 bits. + /// + Alphanumeric = 2, - /// - /// Byte encoding mode, primarily using the ISO-8859-1 character set. Each character is encoded into 8 bits. - /// When combined with ECI, it can be adapted to use other character sets. - /// - Byte = 4, + /// + /// Byte encoding mode, primarily using the ISO-8859-1 character set. Each character is encoded into 8 bits. + /// When combined with ECI, it can be adapted to use other character sets. + /// + Byte = 4, - /// - /// Kanji encoding mode, which is used to encode characters from the Shift JIS character set, primarily for Japanese Kanji and Kana characters. - /// One character is encoded into 13 bits. This mode is not currently supported by QRCoder. - /// - Kanji = 8, + /// + /// Kanji encoding mode, which is used to encode characters from the Shift JIS character set, primarily for Japanese Kanji and Kana characters. + /// One character is encoded into 13 bits. This mode is not currently supported by QRCoder. + /// + Kanji = 8, - /// - /// Extended Channel Interpretation (ECI) mode, which specifies a character set via an 8-bit number followed by one of the other encoding modes. - /// This allows adapting the byte encoding to accommodate various global text encodings. - /// - ECI = 7 - } + /// + /// Extended Channel Interpretation (ECI) mode, which specifies a character set via an 8-bit number followed by one of the other encoding modes. + /// This allows adapting the byte encoding to accommodate various global text encodings. + /// + ECI = 7 } } diff --git a/QRCoder/QRCodeGenerator/ModulePlacer.BlockedModules.cs b/QRCoder/QRCodeGenerator/ModulePlacer.BlockedModules.cs index 94f4a1db..3a79ed5c 100644 --- a/QRCoder/QRCodeGenerator/ModulePlacer.BlockedModules.cs +++ b/QRCoder/QRCodeGenerator/ModulePlacer.BlockedModules.cs @@ -1,101 +1,94 @@ -using System; +using System; using System.Collections; using System.Threading; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + private static partial class ModulePlacer { - private static partial class ModulePlacer + /// + /// Struct that represents blocked modules using rectangles. + /// + public struct BlockedModules : IDisposable { + private readonly BitArray[] _blockedModules; + + private static BitArray[]? _staticBlockedModules; + /// - /// Struct that represents blocked modules using rectangles. + /// Initializes a new instance of the struct with a specified capacity. /// - public struct BlockedModules : IDisposable + /// The initial capacity of the blocked modules list. + public BlockedModules(int size) { - private readonly BitArray[] _blockedModules; - - private static BitArray[]? _staticBlockedModules; - - /// - /// Initializes a new instance of the struct with a specified capacity. - /// - /// The initial capacity of the blocked modules list. - public BlockedModules(int size) + _blockedModules = Interlocked.Exchange(ref _staticBlockedModules, null)!; + if (_blockedModules != null && _blockedModules.Length >= size) { - _blockedModules = Interlocked.Exchange(ref _staticBlockedModules, null)!; - if (_blockedModules != null && _blockedModules.Length >= size) - { - for (int i = 0; i < size; i++) - _blockedModules[i].SetAll(false); - } - else - { - _blockedModules = new BitArray[size]; - for (int i = 0; i < size; i++) - _blockedModules[i] = new BitArray(size); - } + for (int i = 0; i < size; i++) + _blockedModules[i].SetAll(false); } - - /// - /// Adds a blocked module at the specified coordinates. - /// - /// The x-coordinate of the module. - /// The y-coordinate of the module. - public void Add(int x, int y) + else { - _blockedModules[y][x] = true; + _blockedModules = new BitArray[size]; + for (int i = 0; i < size; i++) + _blockedModules[i] = new BitArray(size); } + } - /// - /// Adds a blocked module defined by the specified rectangle. - /// - /// The rectangle that defines the blocked module. - public void Add(Rectangle rect) + /// + /// Adds a blocked module at the specified coordinates. + /// + /// The x-coordinate of the module. + /// The y-coordinate of the module. + public void Add(int x, int y) + => _blockedModules[y][x] = true; + + /// + /// Adds a blocked module defined by the specified rectangle. + /// + /// The rectangle that defines the blocked module. + public void Add(Rectangle rect) + { + for (int y = rect.Y; y < rect.Y + rect.Height; y++) { - for (int y = rect.Y; y < rect.Y + rect.Height; y++) + for (int x = rect.X; x < rect.X + rect.Width; x++) { - for (int x = rect.X; x < rect.X + rect.Width; x++) - { - _blockedModules[y][x] = true; - } + _blockedModules[y][x] = true; } } + } - /// - /// Checks if the specified coordinates are blocked. - /// - /// The x-coordinate to check. - /// The y-coordinate to check. - /// true if the coordinates are blocked; otherwise, false. - public bool IsBlocked(int x, int y) - { - return _blockedModules[y][x]; - } + /// + /// Checks if the specified coordinates are blocked. + /// + /// The x-coordinate to check. + /// The y-coordinate to check. + /// true if the coordinates are blocked; otherwise, false. + public bool IsBlocked(int x, int y) + => _blockedModules[y][x]; - /// - /// Checks if the specified rectangle is blocked. - /// - /// The rectangle to check. - /// true if the rectangle is blocked; otherwise, false. - public bool IsBlocked(Rectangle r1) + /// + /// Checks if the specified rectangle is blocked. + /// + /// The rectangle to check. + /// true if the rectangle is blocked; otherwise, false. + public bool IsBlocked(Rectangle r1) + { + for (int y = r1.Y; y < r1.Y + r1.Height; y++) { - for (int y = r1.Y; y < r1.Y + r1.Height; y++) + for (int x = r1.X; x < r1.X + r1.Width; x++) { - for (int x = r1.X; x < r1.X + r1.Width; x++) - { - if (_blockedModules[y][x]) - return true; - } + if (_blockedModules[y][x]) + return true; } - return false; - } - - public void Dispose() - { - Interlocked.CompareExchange(ref _staticBlockedModules, _blockedModules, null); } + return false; } + + public void Dispose() + => Interlocked.CompareExchange(ref _staticBlockedModules, _blockedModules, null); } } } diff --git a/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs b/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs index 995dd54f..4b46dac6 100644 --- a/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs +++ b/QRCoder/QRCodeGenerator/ModulePlacer.MaskPattern.cs @@ -1,236 +1,218 @@ -using System; -using System.Collections; +using System; using System.Collections.Generic; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + private static partial class ModulePlacer { - private static partial class ModulePlacer + /// + /// Provides static methods and properties to handle mask patterns used in QR code generation. + /// Mask patterns are applied to QR codes to break up patterns in the data matrix that might confuse scanners. + /// + private static class MaskPattern { /// - /// Provides static methods and properties to handle mask patterns used in QR code generation. - /// Mask patterns are applied to QR codes to break up patterns in the data matrix that might confuse scanners. + /// A dictionary mapping each mask pattern index to its corresponding function that calculates whether a given pixel should be masked. /// - private static class MaskPattern - { - /// - /// A dictionary mapping each mask pattern index to its corresponding function that calculates whether a given pixel should be masked. - /// - public static readonly List> Patterns = - new List>(8) { - MaskPattern.Pattern1, MaskPattern.Pattern2, MaskPattern.Pattern3, MaskPattern.Pattern4, - MaskPattern.Pattern5, MaskPattern.Pattern6, MaskPattern.Pattern7, MaskPattern.Pattern8 - }; - - /// - /// Mask pattern 1: (x + y) % 2 == 0 - /// Applies a checkerboard mask on the QR code. - /// - public static bool Pattern1(int x, int y) - { - return (x + y) % 2 == 0; - } + public static readonly List> Patterns = + new List>(8) { + MaskPattern.Pattern1, MaskPattern.Pattern2, MaskPattern.Pattern3, MaskPattern.Pattern4, + MaskPattern.Pattern5, MaskPattern.Pattern6, MaskPattern.Pattern7, MaskPattern.Pattern8 + }; - /// - /// Mask pattern 2: y % 2 == 0 - /// Applies a horizontal striping mask on the QR code. - /// - public static bool Pattern2(int x, int y) - { - return y % 2 == 0; - } + /// + /// Mask pattern 1: (x + y) % 2 == 0 + /// Applies a checkerboard mask on the QR code. + /// + public static bool Pattern1(int x, int y) + => (x + y) % 2 == 0; - /// - /// Mask pattern 3: x % 3 == 0 - /// Applies a vertical striping mask on the QR code. - /// - public static bool Pattern3(int x, int y) - { - return x % 3 == 0; - } + /// + /// Mask pattern 2: y % 2 == 0 + /// Applies a horizontal striping mask on the QR code. + /// + public static bool Pattern2(int x, int y) + => y % 2 == 0; - /// - /// Mask pattern 4: (x + y) % 3 == 0 - /// Applies a diagonal striping mask on the QR code. - /// - public static bool Pattern4(int x, int y) - { - return (x + y) % 3 == 0; - } + /// + /// Mask pattern 3: x % 3 == 0 + /// Applies a vertical striping mask on the QR code. + /// + public static bool Pattern3(int x, int y) + => x % 3 == 0; - /// - /// Mask pattern 5: ((y / 2) + (x / 3)) % 2 == 0 - /// Applies a complex pattern mask on the QR code, mixing horizontal and vertical rules. - /// - public static bool Pattern5(int x, int y) - { - return ((int)(Math.Floor(y / 2d) + Math.Floor(x / 3d)) % 2) == 0; - } + /// + /// Mask pattern 4: (x + y) % 3 == 0 + /// Applies a diagonal striping mask on the QR code. + /// + public static bool Pattern4(int x, int y) + => (x + y) % 3 == 0; - /// - /// Mask pattern 6: ((x * y) % 2 + (x * y) % 3) == 0 - /// Applies a mask based on the product of x and y coordinates modulo 2 and 3. - /// - public static bool Pattern6(int x, int y) - { - return ((x * y) % 2) + ((x * y) % 3) == 0; - } + /// + /// Mask pattern 5: ((y / 2) + (x / 3)) % 2 == 0 + /// Applies a complex pattern mask on the QR code, mixing horizontal and vertical rules. + /// + public static bool Pattern5(int x, int y) + => ((int)(Math.Floor(y / 2d) + Math.Floor(x / 3d)) % 2) == 0; - /// - /// Mask pattern 7: (((x * y) % 2 + (x * y) % 3) % 2) == 0 - /// Applies a mask based on a more complex function involving the product of x and y coordinates. - /// - public static bool Pattern7(int x, int y) - { - return (((x * y) % 2) + ((x * y) % 3)) % 2 == 0; - } + /// + /// Mask pattern 6: ((x * y) % 2 + (x * y) % 3) == 0 + /// Applies a mask based on the product of x and y coordinates modulo 2 and 3. + /// + public static bool Pattern6(int x, int y) + => ((x * y) % 2) + ((x * y) % 3) == 0; + + /// + /// Mask pattern 7: (((x * y) % 2 + (x * y) % 3) % 2) == 0 + /// Applies a mask based on a more complex function involving the product of x and y coordinates. + /// + public static bool Pattern7(int x, int y) + => (((x * y) % 2) + ((x * y) % 3)) % 2 == 0; + + /// + /// Mask pattern 8: (((x + y) % 2) + ((x * y) % 3) % 2) == 0 + /// Combines rules of checkers and complex multiplicative masks. + /// + public static bool Pattern8(int x, int y) + => (((x + y) % 2) + ((x * y) % 3)) % 2 == 0; - /// - /// Mask pattern 8: (((x + y) % 2) + ((x * y) % 3) % 2) == 0 - /// Combines rules of checkers and complex multiplicative masks. - /// - public static bool Pattern8(int x, int y) + /// + /// Calculates a penalty score for a QR code to evaluate the effectiveness of a mask pattern. + /// A lower score indicates a QR code that is easier for decoders to read accurately. + /// The score is the sum of four penalty rules applied to the QR code. + /// + /// The QR code data structure to be evaluated. + /// The total penalty score of the QR code. + public static int Score(QRCodeData qrCode) + { + int score1 = 0, // Penalty for groups of five or more same-color modules in a row (or column) + score2 = 0, // Penalty for blocks of modules in the same color + score3 = 0, // Penalty for specific patterns found within the QR code + score4 = 0; // Penalty for having more than 50% black modules or more than 50% white modules + var size = qrCode.ModuleMatrix.Count; + + //Penalty 1: Checking for consecutive modules of the same color in rows and columns + for (var y = 0; y < size; y++) { - return (((x + y) % 2) + ((x * y) % 3)) % 2 == 0; + var modInRow = 0; + var modInColumn = 0; + var lastValRow = qrCode.ModuleMatrix[y][0]; + var lastValColumn = qrCode.ModuleMatrix[0][y]; + for (var x = 0; x < size; x++) + { + // Check rows for consecutive modules + if (qrCode.ModuleMatrix[y][x] == lastValRow) + modInRow++; + else + modInRow = 1; + if (modInRow == 5) + score1 += 3; + else if (modInRow > 5) + score1++; + lastValRow = qrCode.ModuleMatrix[y][x]; + + // Check columns for consecutive modules + if (qrCode.ModuleMatrix[x][y] == lastValColumn) + modInColumn++; + else + modInColumn = 1; + if (modInColumn == 5) + score1 += 3; + else if (modInColumn > 5) + score1++; + lastValColumn = qrCode.ModuleMatrix[x][y]; + } } - /// - /// Calculates a penalty score for a QR code to evaluate the effectiveness of a mask pattern. - /// A lower score indicates a QR code that is easier for decoders to read accurately. - /// The score is the sum of four penalty rules applied to the QR code. - /// - /// The QR code data structure to be evaluated. - /// The total penalty score of the QR code. - public static int Score(QRCodeData qrCode) + //Penalty 2: Checking for blocks of modules in the same color + for (var y = 0; y < size - 1; y++) { - int score1 = 0, // Penalty for groups of five or more same-color modules in a row (or column) - score2 = 0, // Penalty for blocks of modules in the same color - score3 = 0, // Penalty for specific patterns found within the QR code - score4 = 0; // Penalty for having more than 50% black modules or more than 50% white modules - var size = qrCode.ModuleMatrix.Count; - - //Penalty 1: Checking for consecutive modules of the same color in rows and columns - for (var y = 0; y < size; y++) + for (var x = 0; x < size - 1; x++) { - var modInRow = 0; - var modInColumn = 0; - var lastValRow = qrCode.ModuleMatrix[y][0]; - var lastValColumn = qrCode.ModuleMatrix[0][y]; - for (var x = 0; x < size; x++) - { - // Check rows for consecutive modules - if (qrCode.ModuleMatrix[y][x] == lastValRow) - modInRow++; - else - modInRow = 1; - if (modInRow == 5) - score1 += 3; - else if (modInRow > 5) - score1++; - lastValRow = qrCode.ModuleMatrix[y][x]; - - // Check columns for consecutive modules - if (qrCode.ModuleMatrix[x][y] == lastValColumn) - modInColumn++; - else - modInColumn = 1; - if (modInColumn == 5) - score1 += 3; - else if (modInColumn > 5) - score1++; - lastValColumn = qrCode.ModuleMatrix[x][y]; - } + if (qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y][x + 1] && + qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x] && + qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x + 1]) + score2 += 3; } + } - //Penalty 2: Checking for blocks of modules in the same color - for (var y = 0; y < size - 1; y++) + //Penalty 3: Checking for specific patterns within the QR code (patterns that should be avoided) + for (var y = 0; y < size; y++) + { + for (var x = 0; x < size - 10; x++) { - for (var x = 0; x < size - 1; x++) + // Horizontal pattern matching + if ((qrCode.ModuleMatrix[y][x] && + !qrCode.ModuleMatrix[y][x + 1] && + qrCode.ModuleMatrix[y][x + 2] && + qrCode.ModuleMatrix[y][x + 3] && + qrCode.ModuleMatrix[y][x + 4] && + !qrCode.ModuleMatrix[y][x + 5] && + qrCode.ModuleMatrix[y][x + 6] && + !qrCode.ModuleMatrix[y][x + 7] && + !qrCode.ModuleMatrix[y][x + 8] && + !qrCode.ModuleMatrix[y][x + 9] && + !qrCode.ModuleMatrix[y][x + 10]) || + (!qrCode.ModuleMatrix[y][x] && + !qrCode.ModuleMatrix[y][x + 1] && + !qrCode.ModuleMatrix[y][x + 2] && + !qrCode.ModuleMatrix[y][x + 3] && + qrCode.ModuleMatrix[y][x + 4] && + !qrCode.ModuleMatrix[y][x + 5] && + qrCode.ModuleMatrix[y][x + 6] && + qrCode.ModuleMatrix[y][x + 7] && + qrCode.ModuleMatrix[y][x + 8] && + !qrCode.ModuleMatrix[y][x + 9] && + qrCode.ModuleMatrix[y][x + 10])) { - if (qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y][x + 1] && - qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x] && - qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x + 1]) - score2 += 3; + score3 += 40; } - } - //Penalty 3: Checking for specific patterns within the QR code (patterns that should be avoided) - for (var y = 0; y < size; y++) - { - for (var x = 0; x < size - 10; x++) + // Vertical pattern matching + if ((qrCode.ModuleMatrix[x][y] && + !qrCode.ModuleMatrix[x + 1][y] && + qrCode.ModuleMatrix[x + 2][y] && + qrCode.ModuleMatrix[x + 3][y] && + qrCode.ModuleMatrix[x + 4][y] && + !qrCode.ModuleMatrix[x + 5][y] && + qrCode.ModuleMatrix[x + 6][y] && + !qrCode.ModuleMatrix[x + 7][y] && + !qrCode.ModuleMatrix[x + 8][y] && + !qrCode.ModuleMatrix[x + 9][y] && + !qrCode.ModuleMatrix[x + 10][y]) || + (!qrCode.ModuleMatrix[x][y] && + !qrCode.ModuleMatrix[x + 1][y] && + !qrCode.ModuleMatrix[x + 2][y] && + !qrCode.ModuleMatrix[x + 3][y] && + qrCode.ModuleMatrix[x + 4][y] && + !qrCode.ModuleMatrix[x + 5][y] && + qrCode.ModuleMatrix[x + 6][y] && + qrCode.ModuleMatrix[x + 7][y] && + qrCode.ModuleMatrix[x + 8][y] && + !qrCode.ModuleMatrix[x + 9][y] && + qrCode.ModuleMatrix[x + 10][y])) { - // Horizontal pattern matching - if ((qrCode.ModuleMatrix[y][x] && - !qrCode.ModuleMatrix[y][x + 1] && - qrCode.ModuleMatrix[y][x + 2] && - qrCode.ModuleMatrix[y][x + 3] && - qrCode.ModuleMatrix[y][x + 4] && - !qrCode.ModuleMatrix[y][x + 5] && - qrCode.ModuleMatrix[y][x + 6] && - !qrCode.ModuleMatrix[y][x + 7] && - !qrCode.ModuleMatrix[y][x + 8] && - !qrCode.ModuleMatrix[y][x + 9] && - !qrCode.ModuleMatrix[y][x + 10]) || - (!qrCode.ModuleMatrix[y][x] && - !qrCode.ModuleMatrix[y][x + 1] && - !qrCode.ModuleMatrix[y][x + 2] && - !qrCode.ModuleMatrix[y][x + 3] && - qrCode.ModuleMatrix[y][x + 4] && - !qrCode.ModuleMatrix[y][x + 5] && - qrCode.ModuleMatrix[y][x + 6] && - qrCode.ModuleMatrix[y][x + 7] && - qrCode.ModuleMatrix[y][x + 8] && - !qrCode.ModuleMatrix[y][x + 9] && - qrCode.ModuleMatrix[y][x + 10])) - { - score3 += 40; - } - - // Vertical pattern matching - if ((qrCode.ModuleMatrix[x][y] && - !qrCode.ModuleMatrix[x + 1][y] && - qrCode.ModuleMatrix[x + 2][y] && - qrCode.ModuleMatrix[x + 3][y] && - qrCode.ModuleMatrix[x + 4][y] && - !qrCode.ModuleMatrix[x + 5][y] && - qrCode.ModuleMatrix[x + 6][y] && - !qrCode.ModuleMatrix[x + 7][y] && - !qrCode.ModuleMatrix[x + 8][y] && - !qrCode.ModuleMatrix[x + 9][y] && - !qrCode.ModuleMatrix[x + 10][y]) || - (!qrCode.ModuleMatrix[x][y] && - !qrCode.ModuleMatrix[x + 1][y] && - !qrCode.ModuleMatrix[x + 2][y] && - !qrCode.ModuleMatrix[x + 3][y] && - qrCode.ModuleMatrix[x + 4][y] && - !qrCode.ModuleMatrix[x + 5][y] && - qrCode.ModuleMatrix[x + 6][y] && - qrCode.ModuleMatrix[x + 7][y] && - qrCode.ModuleMatrix[x + 8][y] && - !qrCode.ModuleMatrix[x + 9][y] && - qrCode.ModuleMatrix[x + 10][y])) - { - score3 += 40; - } + score3 += 40; } } + } - //Penalty 4: Proportions of dark and light modules - int blackModules = 0; - foreach (var bitArray in qrCode.ModuleMatrix) - for (var x = 0; x < size; x++) - if (bitArray[x]) - blackModules++; + //Penalty 4: Proportions of dark and light modules + int blackModules = 0; + foreach (var bitArray in qrCode.ModuleMatrix) + for (var x = 0; x < size; x++) + if (bitArray[x]) + blackModules++; - var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); - var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); - var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; + var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); + var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); + var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); + score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; - // Return the sum of all four penalties - return (score1 + score2) + (score3 + score4); - } + // Return the sum of all four penalties + return (score1 + score2) + (score3 + score4); } } } diff --git a/QRCoder/QRCodeGenerator/ModulePlacer.cs b/QRCoder/QRCodeGenerator/ModulePlacer.cs index f83142b1..f452e6b3 100644 --- a/QRCoder/QRCodeGenerator/ModulePlacer.cs +++ b/QRCoder/QRCodeGenerator/ModulePlacer.cs @@ -1,377 +1,375 @@ -using System; -using System.Collections.Generic; using System.Collections; +using System.Collections.Generic; + +namespace QRCoder; -namespace QRCoder +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + private static partial class ModulePlacer { - private static partial class ModulePlacer + /// + /// Places the version information on the QR code matrix for versions 7 and higher. Version information + /// is encoded into two small rectangular areas near the bottom left and top right corners outside the timing patterns. + /// + /// The QR code data structure to modify. + /// The bit array containing the version information. + public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr, bool offset) { - /// - /// Places the version information on the QR code matrix for versions 7 and higher. Version information - /// is encoded into two small rectangular areas near the bottom left and top right corners outside the timing patterns. - /// - /// The QR code data structure to modify. - /// The bit array containing the version information. - public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr, bool offset) - { - var offsetValue = offset ? 4 : 0; - var size = qrCode.ModuleMatrix.Count - offsetValue - offsetValue; - - // Loop through each module position intended for version information, placed adjacent to the separators. - for (var x = 0; x < 6; x++) - { - for (var y = 0; y < 3; y++) - { - // Apply the version bits to the corresponding modules on the matrix, mapping the bits from the versionStr array. - qrCode.ModuleMatrix[y + size - 11 + offsetValue][x + offsetValue] = versionStr[17 - (x * 3 + y)]; - qrCode.ModuleMatrix[x + offsetValue][y + size - 11 + offsetValue] = versionStr[17 - (x * 3 + y)]; - } - } - } + var offsetValue = offset ? 4 : 0; + var size = qrCode.ModuleMatrix.Count - offsetValue - offsetValue; - /// - /// Places the format information on the QR code, encoding the error correction level and mask pattern used. - /// - /// The QR code data structure to modify. - /// The bit array containing the format information. - public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr, bool offset) + // Loop through each module position intended for version information, placed adjacent to the separators. + for (var x = 0; x < 6; x++) { - var offsetValue = offset ? 4 : 0; - var size = qrCode.ModuleMatrix.Count - offsetValue - offsetValue; - - // { x1, y1, x2, y2 } i - // =============================== - // { 8, 0, size - 1, 8 }, // 0 - // { 8, 1, size - 2, 8 }, // 1 - // { 8, 2, size - 3, 8 }, // 2 - // { 8, 3, size - 4, 8 }, // 3 - // { 8, 4, size - 5, 8 }, // 4 - // { 8, 5, size - 6, 8 }, // 5 - // { 8, 7, size - 7, 8 }, // 6 - // { 8, 8, size - 8, 8 }, // 7 - // { 7, 8, 8, size - 7 }, // 8 - // { 5, 8, 8, size - 6 }, // 9 - // { 4, 8, 8, size - 5 }, // 10 - // { 3, 8, 8, size - 4 }, // 11 - // { 2, 8, 8, size - 3 }, // 12 - // { 1, 8, 8, size - 2 }, // 13 - // { 0, 8, 8, size - 1 } }; // 14 - - for (var i = 0; i < 15; i++) + for (var y = 0; y < 3; y++) { - // values computed to follow table above - var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i; - var y1 = i < 6 ? i : i < 7 ? i + 1 : 8; - var x2 = i < 8 ? size - 1 - i : 8; - var y2 = i < 8 ? 8 : size - (15 - i); - - qrCode.ModuleMatrix[y1 + offsetValue][x1 + offsetValue] = formatStr[14 - i]; - qrCode.ModuleMatrix[y2 + offsetValue][x2 + offsetValue] = formatStr[14 - i]; + // Apply the version bits to the corresponding modules on the matrix, mapping the bits from the versionStr array. + qrCode.ModuleMatrix[y + size - 11 + offsetValue][x + offsetValue] = versionStr[17 - (x * 3 + y)]; + qrCode.ModuleMatrix[x + offsetValue][y + size - 11 + offsetValue] = versionStr[17 - (x * 3 + y)]; } } + } - /// - /// Applies the most effective mask pattern to the QR code based on minimizing the penalty score, - /// which evaluates how well the pattern will work for QR scanners. - /// - /// The QR code data structure where the mask will be applied. - /// The version of the QR code, which determines the size and complexity. - /// List of rectangles representing areas that must not be overwritten. - /// The error correction level of the QR code, which affects format string values. - /// The index of the selected mask pattern. - public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blockedModules, ECCLevel eccLevel) + /// + /// Places the format information on the QR code, encoding the error correction level and mask pattern used. + /// + /// The QR code data structure to modify. + /// The bit array containing the format information. + public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr, bool offset) + { + var offsetValue = offset ? 4 : 0; + var size = qrCode.ModuleMatrix.Count - offsetValue - offsetValue; + + // { x1, y1, x2, y2 } i + // =============================== + // { 8, 0, size - 1, 8 }, // 0 + // { 8, 1, size - 2, 8 }, // 1 + // { 8, 2, size - 3, 8 }, // 2 + // { 8, 3, size - 4, 8 }, // 3 + // { 8, 4, size - 5, 8 }, // 4 + // { 8, 5, size - 6, 8 }, // 5 + // { 8, 7, size - 7, 8 }, // 6 + // { 8, 8, size - 8, 8 }, // 7 + // { 7, 8, 8, size - 7 }, // 8 + // { 5, 8, 8, size - 6 }, // 9 + // { 4, 8, 8, size - 5 }, // 10 + // { 3, 8, 8, size - 4 }, // 11 + // { 2, 8, 8, size - 3 }, // 12 + // { 1, 8, 8, size - 2 }, // 13 + // { 0, 8, 8, size - 1 } }; // 14 + + for (var i = 0; i < 15; i++) { - int selectedPattern = -1; // no pattern selected yet - var patternScore = int.MaxValue; // lower score is better - - var size = qrCode.ModuleMatrix.Count - 8; - - // Temporary QRCodeData object to test different mask patterns without altering the original. - var qrTemp = new QRCodeData(version, false); - BitArray? versionString = null; - if (version >= 7) - { - versionString = new BitArray(18); - GetVersionString(versionString, version); - } - var formatStr = new BitArray(15); - for (var maskPattern = 0; maskPattern < 8; maskPattern++) - { - var patternFunc = MaskPattern.Patterns[maskPattern]; + // values computed to follow table above + var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i; + var y1 = i < 6 ? i : i < 7 ? i + 1 : 8; + var x2 = i < 8 ? size - 1 - i : 8; + var y2 = i < 8 ? 8 : size - (15 - i); + + qrCode.ModuleMatrix[y1 + offsetValue][x1 + offsetValue] = formatStr[14 - i]; + qrCode.ModuleMatrix[y2 + offsetValue][x2 + offsetValue] = formatStr[14 - i]; + } + } - // Reset the temporary QR code to the current state of the actual QR code. - for (var y = 0; y < size; y++) - { - for (var x = 0; x < size; x++) - { - qrTemp.ModuleMatrix[y][x] = qrCode.ModuleMatrix[y + 4][x + 4]; - } - } + /// + /// Applies the most effective mask pattern to the QR code based on minimizing the penalty score, + /// which evaluates how well the pattern will work for QR scanners. + /// + /// The QR code data structure where the mask will be applied. + /// The version of the QR code, which determines the size and complexity. + /// List of rectangles representing areas that must not be overwritten. + /// The error correction level of the QR code, which affects format string values. + /// The index of the selected mask pattern. + public static int MaskCode(QRCodeData qrCode, int version, BlockedModules blockedModules, ECCLevel eccLevel) + { + int selectedPattern = -1; // no pattern selected yet + var patternScore = int.MaxValue; // lower score is better - // Place format information using the current mask pattern. - GetFormatString(formatStr, eccLevel, maskPattern); - ModulePlacer.PlaceFormat(qrTemp, formatStr, false); + var size = qrCode.ModuleMatrix.Count - 8; - // Place version information if applicable. - if (versionString != null) // aka if (version >= 7) - { - ModulePlacer.PlaceVersion(qrTemp, versionString, false); - } + // Temporary QRCodeData object to test different mask patterns without altering the original. + var qrTemp = new QRCodeData(version, false); + BitArray? versionString = null; + if (version >= 7) + { + versionString = new BitArray(18); + GetVersionString(versionString, version); + } + var formatStr = new BitArray(15); + for (var maskPattern = 0; maskPattern < 8; maskPattern++) + { + var patternFunc = MaskPattern.Patterns[maskPattern]; - // Apply the mask pattern and calculate the score. + // Reset the temporary QR code to the current state of the actual QR code. + for (var y = 0; y < size; y++) + { for (var x = 0; x < size; x++) { - for (var y = 0; y < x; y++) - { - if (!blockedModules.IsBlocked(x, y)) - { - qrTemp.ModuleMatrix[y][x] ^= patternFunc(x, y); - qrTemp.ModuleMatrix[x][y] ^= patternFunc(y, x); - } - } - - if (!blockedModules.IsBlocked(x, x)) - { - qrTemp.ModuleMatrix[x][x] ^= patternFunc(x, x); - } + qrTemp.ModuleMatrix[y][x] = qrCode.ModuleMatrix[y + 4][x + 4]; } + } - var score = MaskPattern.Score(qrTemp); + // Place format information using the current mask pattern. + GetFormatString(formatStr, eccLevel, maskPattern); + ModulePlacer.PlaceFormat(qrTemp, formatStr, false); - // Select the pattern with the lowest score, indicating better QR code readability. - if (patternScore > score) - { - selectedPattern = maskPattern; - patternScore = score; - } + // Place version information if applicable. + if (versionString != null) // aka if (version >= 7) + { + ModulePlacer.PlaceVersion(qrTemp, versionString, false); } - // Apply the best mask pattern to the actual QR code. - var selectedPatternFunc = MaskPattern.Patterns[selectedPattern]; + // Apply the mask pattern and calculate the score. for (var x = 0; x < size; x++) { for (var y = 0; y < x; y++) { if (!blockedModules.IsBlocked(x, y)) { - qrCode.ModuleMatrix[y + 4][x + 4] ^= selectedPatternFunc(x, y); - qrCode.ModuleMatrix[x + 4][y + 4] ^= selectedPatternFunc(y, x); + qrTemp.ModuleMatrix[y][x] ^= patternFunc(x, y); + qrTemp.ModuleMatrix[x][y] ^= patternFunc(y, x); } } if (!blockedModules.IsBlocked(x, x)) { - qrCode.ModuleMatrix[x + 4][x + 4] ^= selectedPatternFunc(x, x); + qrTemp.ModuleMatrix[x][x] ^= patternFunc(x, x); } } - return selectedPattern; + var score = MaskPattern.Score(qrTemp); + + // Select the pattern with the lowest score, indicating better QR code readability. + if (patternScore > score) + { + selectedPattern = maskPattern; + patternScore = score; + } } - /// - /// Places data bits into the QR code's module matrix following a specific pattern that navigates around blocked modules. - /// - /// The QR code data structure where the data bits are to be placed. - /// The data bits to be placed within the QR code matrix. - /// A list of rectangles representing areas within the QR code matrix that should not be modified because they contain other necessary information like format and version info. - public static void PlaceDataWords(QRCodeData qrCode, BitArray data, BlockedModules blockedModules) + // Apply the best mask pattern to the actual QR code. + var selectedPatternFunc = MaskPattern.Patterns[selectedPattern]; + for (var x = 0; x < size; x++) { - var size = qrCode.ModuleMatrix.Count - 8; // Get the size of the QR code matrix. - var up = true; // A boolean flag used to alternate the direction of filling data: up or down. - var index = 0; // Index to track the current bit position in the data BitArray. - var count = data.Length; // Total number of data bits to place. - - // Loop from the rightmost column to the leftmost column, skipping one column each time. - for (var x = size - 1; x >= 0; x -= 2) + for (var y = 0; y < x; y++) { - // Skip the timing pattern column at position 6. - if (x == 6) - x = 5; - - // Loop through each row in the current column set. - for (var yMod = 1; yMod <= size; yMod++) + if (!blockedModules.IsBlocked(x, y)) { - int y; // Actual y position to place data in the matrix. - - // Determine the actual y position based on the current fill direction. - if (up) - { - y = size - yMod; // Calculate y for upward direction. - // Place data if within data length, current position is not blocked, and leftward column is in bounds. - if (index < count && !blockedModules.IsBlocked(x, y)) - qrCode.ModuleMatrix[y + 4][x + 4] = data[index++]; - if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y)) - qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++]; - } - else - { - y = yMod - 1; // Calculate y for downward direction. - // Similar checks and data placement for the downward direction. - if (index < count && !blockedModules.IsBlocked(x, y)) - qrCode.ModuleMatrix[y + 4][x + 4] = data[index++]; - if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y)) - qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++]; - } + qrCode.ModuleMatrix[y + 4][x + 4] ^= selectedPatternFunc(x, y); + qrCode.ModuleMatrix[x + 4][y + 4] ^= selectedPatternFunc(y, x); } - // Switch the fill direction after completing each column set. - up = !up; } - } - /// - /// Reserves separator areas around the positioning patterns of a QR code to ensure that these crucial areas remain unmodified during data placement. - /// - /// The size of the QR code matrix. - /// A list of rectangles representing areas that must not be overwritten. - public static void ReserveSeperatorAreas(int size, BlockedModules blockedModules) - { - // Block areas around the finder patterns, which are located near three corners of the QR code. - blockedModules.Add(new Rectangle(7, 0, 1, 8)); // Vertical block near the top left finder pattern - blockedModules.Add(new Rectangle(0, 7, 7, 1)); // Horizontal block near the top left finder pattern - blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); // Horizontal block near the bottom left finder pattern - blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); // Vertical block near the bottom left finder pattern - blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); // Vertical block near the top right finder pattern - blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); // Horizontal block near the top right finder pattern + if (!blockedModules.IsBlocked(x, x)) + { + qrCode.ModuleMatrix[x + 4][x + 4] ^= selectedPatternFunc(x, x); + } } - /// - /// Reserves areas for version information on QR codes that are version 7 or higher. Also reserves space for format information. - /// - /// The size of the QR code matrix. - /// The version number of the QR code, which determines the placement of version information. - /// A list of rectangles representing areas that must not be overwritten. - public static void ReserveVersionAreas(int size, int version, BlockedModules blockedModules) + return selectedPattern; + } + + /// + /// Places data bits into the QR code's module matrix following a specific pattern that navigates around blocked modules. + /// + /// The QR code data structure where the data bits are to be placed. + /// The data bits to be placed within the QR code matrix. + /// A list of rectangles representing areas within the QR code matrix that should not be modified because they contain other necessary information like format and version info. + public static void PlaceDataWords(QRCodeData qrCode, BitArray data, BlockedModules blockedModules) + { + var size = qrCode.ModuleMatrix.Count - 8; // Get the size of the QR code matrix. + var up = true; // A boolean flag used to alternate the direction of filling data: up or down. + var index = 0; // Index to track the current bit position in the data BitArray. + var count = data.Length; // Total number of data bits to place. + + // Loop from the rightmost column to the leftmost column, skipping one column each time. + for (var x = size - 1; x >= 0; x -= 2) { - // Reserve areas near the timing patterns for version and format information. - blockedModules.Add(new Rectangle(8, 0, 1, 6)); // Near the top timing pattern - blockedModules.Add(new Rectangle(8, 7, 1, 1)); // Small square near the top left finder pattern - blockedModules.Add(new Rectangle(0, 8, 6, 1)); // Near the left timing pattern - blockedModules.Add(new Rectangle(7, 8, 2, 1)); // Extension of the above block - blockedModules.Add(new Rectangle(size - 8, 8, 8, 1)); // Near the right timing pattern - blockedModules.Add(new Rectangle(8, size - 7, 1, 7)); // Near the bottom timing pattern - - // If the version is 7 or higher, additional blocks for version information are added. - if (version >= 7) + // Skip the timing pattern column at position 6. + if (x == 6) + x = 5; + + // Loop through each row in the current column set. + for (var yMod = 1; yMod <= size; yMod++) { - blockedModules.Add(new Rectangle(size - 11, 0, 3, 6)); // Top right version information block - blockedModules.Add(new Rectangle(0, size - 11, 6, 3)); // Bottom left version information block + int y; // Actual y position to place data in the matrix. + + // Determine the actual y position based on the current fill direction. + if (up) + { + y = size - yMod; // Calculate y for upward direction. + // Place data if within data length, current position is not blocked, and leftward column is in bounds. + if (index < count && !blockedModules.IsBlocked(x, y)) + qrCode.ModuleMatrix[y + 4][x + 4] = data[index++]; + if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y)) + qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++]; + } + else + { + y = yMod - 1; // Calculate y for downward direction. + // Similar checks and data placement for the downward direction. + if (index < count && !blockedModules.IsBlocked(x, y)) + qrCode.ModuleMatrix[y + 4][x + 4] = data[index++]; + if (index < count && x > 0 && !blockedModules.IsBlocked(x - 1, y)) + qrCode.ModuleMatrix[y + 4][x - 1 + 4] = data[index++]; + } } + // Switch the fill direction after completing each column set. + up = !up; } + } + + /// + /// Reserves separator areas around the positioning patterns of a QR code to ensure that these crucial areas remain unmodified during data placement. + /// + /// The size of the QR code matrix. + /// A list of rectangles representing areas that must not be overwritten. + public static void ReserveSeperatorAreas(int size, BlockedModules blockedModules) + { + // Block areas around the finder patterns, which are located near three corners of the QR code. + blockedModules.Add(new Rectangle(7, 0, 1, 8)); // Vertical block near the top left finder pattern + blockedModules.Add(new Rectangle(0, 7, 7, 1)); // Horizontal block near the top left finder pattern + blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); // Horizontal block near the bottom left finder pattern + blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); // Vertical block near the bottom left finder pattern + blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); // Vertical block near the top right finder pattern + blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); // Horizontal block near the top right finder pattern + } - /// - /// Places a dark module on the QR code matrix as per QR code specification, which requires a dark module at a specific position for all QR codes. - /// - /// The QR code data structure where the dark module is to be placed. - /// The version number of the QR code, which determines the specific location of the dark module. - /// A list of rectangles representing areas that must not be overwritten, updated to include the dark module. - public static void PlaceDarkModule(QRCodeData qrCode, int version, BlockedModules blockedModules) + /// + /// Reserves areas for version information on QR codes that are version 7 or higher. Also reserves space for format information. + /// + /// The size of the QR code matrix. + /// The version number of the QR code, which determines the placement of version information. + /// A list of rectangles representing areas that must not be overwritten. + public static void ReserveVersionAreas(int size, int version, BlockedModules blockedModules) + { + // Reserve areas near the timing patterns for version and format information. + blockedModules.Add(new Rectangle(8, 0, 1, 6)); // Near the top timing pattern + blockedModules.Add(new Rectangle(8, 7, 1, 1)); // Small square near the top left finder pattern + blockedModules.Add(new Rectangle(0, 8, 6, 1)); // Near the left timing pattern + blockedModules.Add(new Rectangle(7, 8, 2, 1)); // Extension of the above block + blockedModules.Add(new Rectangle(size - 8, 8, 8, 1)); // Near the right timing pattern + blockedModules.Add(new Rectangle(8, size - 7, 1, 7)); // Near the bottom timing pattern + + // If the version is 7 or higher, additional blocks for version information are added. + if (version >= 7) { - // Place the dark module, which is always required to be black. - qrCode.ModuleMatrix[4 * version + 9 + 4][8 + 4] = true; - // Block the dark module area to prevent overwriting during further QR code generation steps. - blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); + blockedModules.Add(new Rectangle(size - 11, 0, 3, 6)); // Top right version information block + blockedModules.Add(new Rectangle(0, size - 11, 6, 3)); // Bottom left version information block } + } + + /// + /// Places a dark module on the QR code matrix as per QR code specification, which requires a dark module at a specific position for all QR codes. + /// + /// The QR code data structure where the dark module is to be placed. + /// The version number of the QR code, which determines the specific location of the dark module. + /// A list of rectangles representing areas that must not be overwritten, updated to include the dark module. + public static void PlaceDarkModule(QRCodeData qrCode, int version, BlockedModules blockedModules) + { + // Place the dark module, which is always required to be black. + qrCode.ModuleMatrix[4 * version + 9 + 4][8 + 4] = true; + // Block the dark module area to prevent overwriting during further QR code generation steps. + blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); + } - /// - /// Places finder patterns on the QR code. Finder patterns are critical for QR code scanners to correctly orient and recognize the QR code. - /// - /// The QR code data structure where the finder patterns will be placed. - /// A list of rectangles representing areas that must not be overwritten. This is updated with the areas occupied by the finder patterns. - public static void PlaceFinderPatterns(QRCodeData qrCode, BlockedModules blockedModules) + /// + /// Places finder patterns on the QR code. Finder patterns are critical for QR code scanners to correctly orient and recognize the QR code. + /// + /// The QR code data structure where the finder patterns will be placed. + /// A list of rectangles representing areas that must not be overwritten. This is updated with the areas occupied by the finder patterns. + public static void PlaceFinderPatterns(QRCodeData qrCode, BlockedModules blockedModules) + { + var size = qrCode.ModuleMatrix.Count - 8; + + // Loop to place three finder patterns in the top-left, top-right, and bottom-left corners of the QR code. + for (var i = 0; i < 3; i++) { - var size = qrCode.ModuleMatrix.Count - 8; + // Calculate the x and y starting positions for each finder pattern based on the index. + var locationX = i == 1 ? size - 7 : 0; // Place at top-right if i is 1, otherwise at left side (top or bottom). + var locationY = i == 2 ? size - 7 : 0; // Place at bottom-left if i is 2, otherwise at top (left or right). - // Loop to place three finder patterns in the top-left, top-right, and bottom-left corners of the QR code. - for (var i = 0; i < 3; i++) + // Nested loops to draw the 7x7 finder pattern at the calculated location. + for (var x = 0; x < 7; x++) { - // Calculate the x and y starting positions for each finder pattern based on the index. - var locationX = i == 1 ? size - 7 : 0; // Place at top-right if i is 1, otherwise at left side (top or bottom). - var locationY = i == 2 ? size - 7 : 0; // Place at bottom-left if i is 2, otherwise at top (left or right). - - // Nested loops to draw the 7x7 finder pattern at the calculated location. - for (var x = 0; x < 7; x++) + for (var y = 0; y < 7; y++) { - for (var y = 0; y < 7; y++) + // Condition to form the characteristic 5x5 black/white border of the finder pattern. + // The center 3x3 area is filled, bordered by a line of white modules, enclosed by a 7x7 black border. + if (!(((x == 1 || x == 5) && y > 0 && y < 6) || (x > 0 && x < 6 && (y == 1 || y == 5)))) { - // Condition to form the characteristic 5x5 black/white border of the finder pattern. - // The center 3x3 area is filled, bordered by a line of white modules, enclosed by a 7x7 black border. - if (!(((x == 1 || x == 5) && y > 0 && y < 6) || (x > 0 && x < 6 && (y == 1 || y == 5)))) - { - qrCode.ModuleMatrix[y + locationY + 4][x + locationX + 4] = true; - } + qrCode.ModuleMatrix[y + locationY + 4][x + locationX + 4] = true; } } - - // Add the area covered by the current finder pattern to the list of blocked modules, preventing any data from being placed there. - blockedModules.Add(new Rectangle(locationX, locationY, 7, 7)); } + + // Add the area covered by the current finder pattern to the list of blocked modules, preventing any data from being placed there. + blockedModules.Add(new Rectangle(locationX, locationY, 7, 7)); } + } - /// - /// Places alignment patterns on the QR code matrix. Alignment patterns help ensure the scanner can correctly interpret the QR code at various scales and orientations. - /// - /// The QR code data structure where the alignment patterns will be placed. - /// A list of points representing the centers of where alignment patterns should be placed. - /// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by alignment patterns. - public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, BlockedModules blockedModules) + /// + /// Places alignment patterns on the QR code matrix. Alignment patterns help ensure the scanner can correctly interpret the QR code at various scales and orientations. + /// + /// The QR code data structure where the alignment patterns will be placed. + /// A list of points representing the centers of where alignment patterns should be placed. + /// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by alignment patterns. + public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, BlockedModules blockedModules) + { + // Iterate through each specified location for alignment patterns. + foreach (var loc in alignmentPatternLocations) { - // Iterate through each specified location for alignment patterns. - foreach (var loc in alignmentPatternLocations) - { - // Define a 5x5 rectangle for the alignment pattern based on the center point provided. - var alignmentPatternRect = new Rectangle(loc.X, loc.Y, 5, 5); + // Define a 5x5 rectangle for the alignment pattern based on the center point provided. + var alignmentPatternRect = new Rectangle(loc.X, loc.Y, 5, 5); - // Check if the proposed alignment pattern rectangle intersects with any already blocked rectangles. - if (blockedModules.IsBlocked(alignmentPatternRect)) - { - // Skip the current location if it is blocked to prevent overwriting crucial information. - continue; - } + // Check if the proposed alignment pattern rectangle intersects with any already blocked rectangles. + if (blockedModules.IsBlocked(alignmentPatternRect)) + { + // Skip the current location if it is blocked to prevent overwriting crucial information. + continue; + } - // Place the alignment pattern by setting modules within the 5x5 area. - // The pattern consists of a 3x3 center block with a single module border. - for (var x = 0; x < 5; x++) + // Place the alignment pattern by setting modules within the 5x5 area. + // The pattern consists of a 3x3 center block with a single module border. + for (var x = 0; x < 5; x++) + { + for (var y = 0; y < 5; y++) { - for (var y = 0; y < 5; y++) + // Create the pattern: a 3x3 block surrounded by a border, with the very center module set. + if (y == 0 || y == 4 || x == 0 || x == 4 || (x == 2 && y == 2)) { - // Create the pattern: a 3x3 block surrounded by a border, with the very center module set. - if (y == 0 || y == 4 || x == 0 || x == 4 || (x == 2 && y == 2)) - { - qrCode.ModuleMatrix[loc.Y + y + 4][loc.X + x + 4] = true; - } + qrCode.ModuleMatrix[loc.Y + y + 4][loc.X + x + 4] = true; } } - - // Add the alignment pattern's area to the list of blocked modules to prevent future overwrites. - blockedModules.Add(new Rectangle(loc.X, loc.Y, 5, 5)); } + + // Add the alignment pattern's area to the list of blocked modules to prevent future overwrites. + blockedModules.Add(new Rectangle(loc.X, loc.Y, 5, 5)); } + } - /// - /// Places timing patterns in the QR code. Timing patterns are alternating dark and light modules that help scanners determine the coordinates of modules within the QR code. - /// - /// The QR code data structure where the timing patterns will be placed. - /// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by timing patterns. - public static void PlaceTimingPatterns(QRCodeData qrCode, BlockedModules blockedModules) - { - var size = qrCode.ModuleMatrix.Count - 8; // Get the size of the QR code matrix. + /// + /// Places timing patterns in the QR code. Timing patterns are alternating dark and light modules that help scanners determine the coordinates of modules within the QR code. + /// + /// The QR code data structure where the timing patterns will be placed. + /// A list of rectangles representing areas that must not be overwritten. Updated with the areas occupied by timing patterns. + public static void PlaceTimingPatterns(QRCodeData qrCode, BlockedModules blockedModules) + { + var size = qrCode.ModuleMatrix.Count - 8; // Get the size of the QR code matrix. - // Place timing patterns starting from the 8th module to the size - 8 to avoid overlapping with finder patterns. - for (var i = 8; i < size - 8; i++) + // Place timing patterns starting from the 8th module to the size - 8 to avoid overlapping with finder patterns. + for (var i = 8; i < size - 8; i++) + { + if (i % 2 == 0) // Place a dark module every other module to create the alternating pattern. { - if (i % 2 == 0) // Place a dark module every other module to create the alternating pattern. - { - qrCode.ModuleMatrix[6 + 4][i + 4] = true; // Horizontal timing pattern - qrCode.ModuleMatrix[i + 4][6 + 4] = true; // Vertical timing pattern - } + qrCode.ModuleMatrix[6 + 4][i + 4] = true; // Horizontal timing pattern + qrCode.ModuleMatrix[i + 4][6 + 4] = true; // Vertical timing pattern } - - // Add the areas occupied by the timing patterns to the list of blocked modules. - blockedModules.Add(new Rectangle(6, 8, 1, size - 16)); // Horizontal timing pattern area - blockedModules.Add(new Rectangle(8, 6, size - 16, 1)); // Vertical timing pattern area } + + // Add the areas occupied by the timing patterns to the list of blocked modules. + blockedModules.Add(new Rectangle(6, 8, 1, size - 16)); // Horizontal timing pattern area + blockedModules.Add(new Rectangle(8, 6, size - 16, 1)); // Vertical timing pattern area } } } diff --git a/QRCoder/QRCodeGenerator/Point.cs b/QRCoder/QRCodeGenerator/Point.cs index ac84f8a4..fc573e45 100644 --- a/QRCoder/QRCodeGenerator/Point.cs +++ b/QRCoder/QRCodeGenerator/Point.cs @@ -1,50 +1,47 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents a 2D point with integer coordinates. + /// + private readonly struct Point : IEquatable { /// - /// Represents a 2D point with integer coordinates. + /// Gets the X-coordinate of the point. /// - private readonly struct Point : IEquatable - { - /// - /// Gets the X-coordinate of the point. - /// - public int X { get; } - - /// - /// Gets the Y-coordinate of the point. - /// - public int Y { get; } + public int X { get; } - /// - /// Initializes a new instance of the struct with specified X and Y coordinates. - /// - /// The X-coordinate of the point. - /// The Y-coordinate of the point. - public Point(int x, int y) - { - this.X = x; - this.Y = y; - } + /// + /// Gets the Y-coordinate of the point. + /// + public int Y { get; } - /// - /// Determines whether the specified is equal to the current . - /// - /// The to compare with the current . - /// True if the specified has the same X and Y coordinates as the current ; otherwise, false. - /// - /// If this method which implements is not implemented, comparisons used by methods such as - /// fall back to reflection, which causes heap allocations internally during the calls to . - /// - public bool Equals(Point other) - { - return this.X == other.X && this.Y == other.Y; - } + /// + /// Initializes a new instance of the struct with specified X and Y coordinates. + /// + /// The X-coordinate of the point. + /// The Y-coordinate of the point. + public Point(int x, int y) + { + X = x; + Y = y; } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// True if the specified has the same X and Y coordinates as the current ; otherwise, false. + /// + /// If this method which implements is not implemented, comparisons used by methods such as + /// fall back to reflection, which causes heap allocations internally during the calls to . + /// + public bool Equals(Point other) + => X == other.X && Y == other.Y; } } diff --git a/QRCoder/QRCodeGenerator/Polynom.cs b/QRCoder/QRCodeGenerator/Polynom.cs index 01a02b62..6fb3c302 100644 --- a/QRCoder/QRCodeGenerator/Polynom.cs +++ b/QRCoder/QRCodeGenerator/Polynom.cs @@ -1,297 +1,284 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents a polynomial, which is a sum of polynomial terms. + /// + private struct Polynom : IDisposable { + private PolynomItem[] _polyItems; + /// - /// Represents a polynomial, which is a sum of polynomial terms. + /// Initializes a new instance of the struct with a specified number of initial capacity for polynomial terms. /// - private struct Polynom : IDisposable + /// The initial capacity of the polynomial items list. + public Polynom(int count) { - private PolynomItem[] _polyItems; - private int _length; - - /// - /// Initializes a new instance of the struct with a specified number of initial capacity for polynomial terms. - /// - /// The initial capacity of the polynomial items list. - public Polynom(int count) - { - _length = 0; - _polyItems = RentArray(count); - } + Count = 0; + _polyItems = RentArray(count); + } - /// - /// Adds a polynomial term to the polynomial. - /// - public void Add(PolynomItem item) - { - AssertCapacity(_length + 1); - _polyItems[_length++] = item; - } + /// + /// Adds a polynomial term to the polynomial. + /// + public void Add(PolynomItem item) + { + AssertCapacity(Count + 1); + _polyItems[Count++] = item; + } - /// - /// Removes the polynomial term at the specified index. - /// - public void RemoveAt(int index) - { - if ((uint)index >= (uint)_length) - throw new IndexOutOfRangeException(); + /// + /// Removes the polynomial term at the specified index. + /// + public void RemoveAt(int index) + { + if ((uint)index >= (uint)Count) + throw new IndexOutOfRangeException(); - if (index < _length - 1) - Array.Copy(_polyItems, index + 1, _polyItems, index, _length - index - 1); + if (index < Count - 1) + Array.Copy(_polyItems, index + 1, _polyItems, index, Count - index - 1); - _length--; - } + Count--; + } - /// - /// Gets or sets a polynomial term at the specified index. - /// - public PolynomItem this[int index] + /// + /// Gets or sets a polynomial term at the specified index. + /// + public PolynomItem this[int index] + { + get { - get { - if ((uint)index >= _length) - ThrowIndexOutOfRangeException(); - return _polyItems[index]; - } - set { - if ((uint)index >= _length) - ThrowIndexOutOfRangeException(); - _polyItems[index] = value; - } + if ((uint)index >= Count) + ThrowIndexOutOfRangeException(); + return _polyItems[index]; + } + set + { + if ((uint)index >= Count) + ThrowIndexOutOfRangeException(); + _polyItems[index] = value; } + } #if NET6_0_OR_GREATER - [StackTraceHidden] + [StackTraceHidden] #endif - private static void ThrowIndexOutOfRangeException() - { - throw new IndexOutOfRangeException(); - } + private static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException(); - /// - /// Gets the number of polynomial terms in the polynomial. - /// - public int Count => _length; + /// + /// Gets the number of polynomial terms in the polynomial. + /// + public int Count { get; private set; } - /// - /// Removes all polynomial terms from the polynomial. - /// - public void Clear() - { - _length = 0; - } + /// + /// Removes all polynomial terms from the polynomial. + /// + public void Clear() => Count = 0; + + /// + /// Clones the polynomial, creating a new instance with the same polynomial terms. + /// + public Polynom Clone() + { + var newPolynom = new Polynom(Count); + Array.Copy(_polyItems, newPolynom._polyItems, Count); + newPolynom.Count = Count; + return newPolynom; + } + + /// + /// Sorts the collection of using a custom comparer function. + /// + /// + /// A function that compares two objects and returns an integer indicating their relative order: + /// less than zero if the first is less than the second, zero if they are equal, or greater than zero if the first is greater than the second. + /// + public void Sort(Func comparer) + { + if (comparer == null) + throw new ArgumentNullException(nameof(comparer)); - /// - /// Clones the polynomial, creating a new instance with the same polynomial terms. - /// - public Polynom Clone() + var items = _polyItems ?? throw new ObjectDisposedException(nameof(Polynom)); + + if (Count <= 1) { - var newPolynom = new Polynom(_length); - Array.Copy(_polyItems, newPolynom._polyItems, _length); - newPolynom._length = _length; - return newPolynom; + return; // Nothing to sort if the list is empty or contains only one element } - /// - /// Sorts the collection of using a custom comparer function. - /// - /// - /// A function that compares two objects and returns an integer indicating their relative order: - /// less than zero if the first is less than the second, zero if they are equal, or greater than zero if the first is greater than the second. - /// - public void Sort(Func comparer) + void QuickSort(int left, int right) { - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); - - var items = _polyItems; - if (items == null) throw new ObjectDisposedException(nameof(Polynom)); - - if (_length <= 1) - { - return; // Nothing to sort if the list is empty or contains only one element - } + int i = left; + int j = right; + var pivot = items[(left + right) / 2]; - void QuickSort(int left, int right) + while (i <= j) { - int i = left; - int j = right; - PolynomItem pivot = items[(left + right) / 2]; + while (comparer(items[i], pivot) < 0) + i++; + while (comparer(items[j], pivot) > 0) + j--; - while (i <= j) + if (i <= j) { - while (comparer(items[i], pivot) < 0) i++; - while (comparer(items[j], pivot) > 0) j--; - - if (i <= j) - { - // Swap items[i] and items[j] - PolynomItem temp = items[i]; - items[i] = items[j]; - items[j] = temp; - i++; - j--; - } + // Swap items[i] and items[j] + var temp = items[i]; + items[i] = items[j]; + items[j] = temp; + i++; + j--; } - - // Recursively sort the sub-arrays - if (left < j) QuickSort(left, j); - if (i < right) QuickSort(i, right); } - QuickSort(0, _length - 1); + // Recursively sort the sub-arrays + if (left < j) + QuickSort(left, j); + if (i < right) + QuickSort(i, right); } - /// - /// Returns a string that represents the polynomial in standard algebraic notation. - /// Example output: "a^2*x^3 + a^5*x^1 + a^3*x^0", which represents the polynomial 2x³ + 5x + 3. - /// - public override string ToString() + QuickSort(0, Count - 1); + } + + /// + /// Returns a string that represents the polynomial in standard algebraic notation. + /// Example output: "a^2*x^3 + a^5*x^1 + a^3*x^0", which represents the polynomial 2x³ + 5x + 3. + /// + public override string ToString() + { + var sb = new StringBuilder(); + + foreach (var polyItem in _polyItems) { - var sb = new StringBuilder(); + sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); + } - foreach (var polyItem in _polyItems) - { - sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); - } + // Remove the trailing " + " if the string builder has added terms + if (sb.Length > 0) + sb.Length -= 3; - // Remove the trailing " + " if the string builder has added terms - if (sb.Length > 0) - sb.Length -= 3; + return sb.ToString(); + } - return sb.ToString(); - } + /// + public void Dispose() + { + ReturnArray(_polyItems); + _polyItems = null!; + } - /// - public void Dispose() + /// + /// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms. + /// + private void AssertCapacity(int min) + { + if (_polyItems.Length < min) { - ReturnArray(_polyItems); - _polyItems = null!; + // All math by QRCoder should be done with fixed polynomials, so we don't need to grow the capacity. + ThrowNotSupportedException(); + + // Sample code for growing the capacity: + //var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 + //Array.Copy(_polyItems, newArray, _length); + //ReturnArray(_polyItems); + //_polyItems = newArray; } - /// - /// Ensures that the polynomial has enough capacity to store the specified number of polynomial terms. - /// - private void AssertCapacity(int min) - { - if (_polyItems.Length < min) - { - // All math by QRCoder should be done with fixed polynomials, so we don't need to grow the capacity. - ThrowNotSupportedException(); - - // Sample code for growing the capacity: - //var newArray = RentArray(Math.Max(min - 1, 8) * 2); // Grow by 2x, but at least by 8 - //Array.Copy(_polyItems, newArray, _length); - //ReturnArray(_polyItems); - //_polyItems = newArray; - } - #if NET6_0_OR_GREATER - [StackTraceHidden] + [StackTraceHidden] #endif - void ThrowNotSupportedException() - { - throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased."); - } - } + void ThrowNotSupportedException() => throw new NotSupportedException("The polynomial capacity is fixed and cannot be increased."); + } #if NETCOREAPP - /// - /// Rents memory for the polynomial terms from the shared memory pool. - /// - private static PolynomItem[] RentArray(int count) - { - return System.Buffers.ArrayPool.Shared.Rent(count); - } + /// + /// Rents memory for the polynomial terms from the shared memory pool. + /// + private static PolynomItem[] RentArray(int count) + => System.Buffers.ArrayPool.Shared.Rent(count); - /// - /// Returns memory allocated for the polynomial terms back to the shared memory pool. - /// - private static void ReturnArray(PolynomItem[] array) - { - System.Buffers.ArrayPool.Shared.Return(array); - } + /// + /// Returns memory allocated for the polynomial terms back to the shared memory pool. + /// + private static void ReturnArray(PolynomItem[] array) + => System.Buffers.ArrayPool.Shared.Return(array); #else - // Implement a poor-man's array pool for .NET Framework - [ThreadStatic] - private static List? _arrayPool; - - /// - /// Rents memory for the polynomial terms from a shared memory pool. - /// - private static PolynomItem[] RentArray(int count) - { - if (count <= 0) - ThrowArgumentOutOfRangeException(); + // Implement a poor-man's array pool for .NET Framework + [ThreadStatic] + private static List? _arrayPool; - // Search for a suitable array in the thread-local pool, if it has been initialized - if (_arrayPool != null) + /// + /// Rents memory for the polynomial terms from a shared memory pool. + /// + private static PolynomItem[] RentArray(int count) + { + if (count <= 0) + ThrowArgumentOutOfRangeException(); + + // Search for a suitable array in the thread-local pool, if it has been initialized + if (_arrayPool != null) + { + for (int i = 0; i < _arrayPool.Count; i++) { - for (int i = 0; i < _arrayPool.Count; i++) + var array = _arrayPool[i]; + if (array.Length >= count) { - var array = _arrayPool[i]; - if (array.Length >= count) - { - _arrayPool.RemoveAt(i); - return array; - } + _arrayPool.RemoveAt(i); + return array; } } + } - // No suitable buffer found; create a new one - return new PolynomItem[count]; + // No suitable buffer found; create a new one + return new PolynomItem[count]; - void ThrowArgumentOutOfRangeException() - { - throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number."); - } - } - - /// - /// Returns memory allocated for the polynomial terms back to a shared memory pool. - /// - private static void ReturnArray(PolynomItem[] array) - { - if (array == null) - return; + void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(count), "The count must be a positive number."); + } + + /// + /// Returns memory allocated for the polynomial terms back to a shared memory pool. + /// + private static void ReturnArray(PolynomItem[] array) + { + if (array == null) + return; - // Initialize the thread-local pool if it's not already done - if (_arrayPool == null) - _arrayPool = new List(8); + // Initialize the thread-local pool if it's not already done + _arrayPool ??= new List(8); - // Add the buffer back to the pool - _arrayPool.Add(array); - } + // Add the buffer back to the pool + _arrayPool.Add(array); + } #endif - /// - /// Returns an enumerator that iterates through the polynomial terms. - /// - public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this); + /// + /// Returns an enumerator that iterates through the polynomial terms. + /// + public PolynumEnumerator GetEnumerator() => new PolynumEnumerator(this); - /// - /// Value type enumerator for the struct. - /// - public struct PolynumEnumerator - { - private Polynom _polynom; - private int _index; + /// + /// Value type enumerator for the struct. + /// + public struct PolynumEnumerator + { + private Polynom _polynom; + private int _index; - public PolynumEnumerator(Polynom polynom) - { - _polynom = polynom; - _index = -1; - } + public PolynumEnumerator(Polynom polynom) + { + _polynom = polynom; + _index = -1; + } - public PolynomItem Current => _polynom[_index]; + public PolynomItem Current => _polynom[_index]; - public bool MoveNext() => ++_index < _polynom._length; - } + public bool MoveNext() => ++_index < _polynom.Count; } } } diff --git a/QRCoder/QRCodeGenerator/PolynomItem.cs b/QRCoder/QRCodeGenerator/PolynomItem.cs index 006c38ab..a881cae0 100644 --- a/QRCoder/QRCodeGenerator/PolynomItem.cs +++ b/QRCoder/QRCodeGenerator/PolynomItem.cs @@ -1,33 +1,32 @@ -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents an individual term of a polynomial, consisting of a coefficient and an exponent. + /// For example, the term 3x² would be represented as a with a coefficient of 3 and an exponent of 2. + /// + private struct PolynomItem { /// - /// Represents an individual term of a polynomial, consisting of a coefficient and an exponent. - /// For example, the term 3x² would be represented as a with a coefficient of 3 and an exponent of 2. + /// Initializes a new instance of the struct with the specified coefficient and exponent. /// - private struct PolynomItem + /// The coefficient of the polynomial term. For example, in the term 3x², the coefficient is 3. + /// The exponent of the polynomial term. For example, in the term 3x², the exponent is 2. + public PolynomItem(int coefficient, int exponent) { - /// - /// Initializes a new instance of the struct with the specified coefficient and exponent. - /// - /// The coefficient of the polynomial term. For example, in the term 3x², the coefficient is 3. - /// The exponent of the polynomial term. For example, in the term 3x², the exponent is 2. - public PolynomItem(int coefficient, int exponent) - { - this.Coefficient = coefficient; - this.Exponent = exponent; - } + Coefficient = coefficient; + Exponent = exponent; + } - /// - /// Gets the coefficient of the polynomial term. - /// - public int Coefficient { get; } + /// + /// Gets the coefficient of the polynomial term. + /// + public int Coefficient { get; } - /// - /// Gets the exponent of the polynomial term. - /// - public int Exponent { get; } - } + /// + /// Gets the exponent of the polynomial term. + /// + public int Exponent { get; } } } diff --git a/QRCoder/QRCodeGenerator/Rectangle.cs b/QRCoder/QRCodeGenerator/Rectangle.cs index c10654d2..0200d24d 100644 --- a/QRCoder/QRCodeGenerator/Rectangle.cs +++ b/QRCoder/QRCodeGenerator/Rectangle.cs @@ -1,46 +1,45 @@ -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents a rectangle defined by its top-left corner's coordinates, width, and height. + /// + private readonly struct Rectangle { /// - /// Represents a rectangle defined by its top-left corner's coordinates, width, and height. + /// Gets the X-coordinate of the top-left corner of the rectangle. /// - private readonly struct Rectangle - { - /// - /// Gets the X-coordinate of the top-left corner of the rectangle. - /// - public int X { get; } + public int X { get; } - /// - /// Gets the Y-coordinate of the top-left corner of the rectangle. - /// - public int Y { get; } + /// + /// Gets the Y-coordinate of the top-left corner of the rectangle. + /// + public int Y { get; } - /// - /// Gets the width of the rectangle. - /// - public int Width { get; } + /// + /// Gets the width of the rectangle. + /// + public int Width { get; } - /// - /// Gets the height of the rectangle. - /// - public int Height { get; } + /// + /// Gets the height of the rectangle. + /// + public int Height { get; } - /// - /// Initializes a new instance of the struct with the specified top-left corner coordinates, width, and height. - /// - /// The X-coordinate of the top-left corner of the rectangle. - /// The Y-coordinate of the top-left corner of the rectangle. - /// The width of the rectangle. - /// The height of the rectangle. - public Rectangle(int x, int y, int w, int h) - { - this.X = x; - this.Y = y; - this.Width = w; - this.Height = h; - } + /// + /// Initializes a new instance of the struct with the specified top-left corner coordinates, width, and height. + /// + /// The X-coordinate of the top-left corner of the rectangle. + /// The Y-coordinate of the top-left corner of the rectangle. + /// The width of the rectangle. + /// The height of the rectangle. + public Rectangle(int x, int y, int w, int h) + { + X = x; + Y = y; + Width = w; + Height = h; } } } diff --git a/QRCoder/QRCodeGenerator/VersionInfo.cs b/QRCoder/QRCodeGenerator/VersionInfo.cs index 76eb74d7..0618bfda 100644 --- a/QRCoder/QRCodeGenerator/VersionInfo.cs +++ b/QRCoder/QRCodeGenerator/VersionInfo.cs @@ -1,34 +1,33 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents version-specific information of a QR code. + /// + private struct VersionInfo { /// - /// Represents version-specific information of a QR code. + /// Initializes a new instance of the struct with a specific version number and its details. /// - private struct VersionInfo + /// The version number of the QR code. Each version has a different module configuration. + /// A list of detailed information related to error correction levels and capacity for each encoding mode. + public VersionInfo(int version, List versionInfoDetails) { - /// - /// Initializes a new instance of the struct with a specific version number and its details. - /// - /// The version number of the QR code. Each version has a different module configuration. - /// A list of detailed information related to error correction levels and capacity for each encoding mode. - public VersionInfo(int version, List versionInfoDetails) - { - this.Version = version; - this.Details = versionInfoDetails; - } + Version = version; + Details = versionInfoDetails; + } - /// - /// Gets the version number of the QR code. Each version number specifies a different size of the QR matrix. - /// - public int Version { get; } + /// + /// Gets the version number of the QR code. Each version number specifies a different size of the QR matrix. + /// + public int Version { get; } - /// - /// Gets a list of details about the QR code version, including the error correction levels and encoding capacities. - /// - public List Details { get; } - } + /// + /// Gets a list of details about the QR code version, including the error correction levels and encoding capacities. + /// + public List Details { get; } } } diff --git a/QRCoder/QRCodeGenerator/VersionInfoDetails.cs b/QRCoder/QRCodeGenerator/VersionInfoDetails.cs index 1cab07ea..f4e315df 100644 --- a/QRCoder/QRCodeGenerator/VersionInfoDetails.cs +++ b/QRCoder/QRCodeGenerator/VersionInfoDetails.cs @@ -1,35 +1,34 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace QRCoder +namespace QRCoder; + +public partial class QRCodeGenerator { - public partial class QRCodeGenerator + /// + /// Represents the detailed information about each error correction level and its corresponding capacities in different encoding modes for a specific version of a QR code. + /// + private struct VersionInfoDetails { /// - /// Represents the detailed information about each error correction level and its corresponding capacities in different encoding modes for a specific version of a QR code. + /// Initializes a new instance of the struct, detailing the error correction level and the capacity for each encoding mode. /// - private struct VersionInfoDetails + /// The error correction level, which determines how much of the code can be restored if the QR code gets damaged. + /// A dictionary mapping each encoding mode to its capacity for the specific error correction level. + public VersionInfoDetails(ECCLevel errorCorrectionLevel, Dictionary capacityDict) { - /// - /// Initializes a new instance of the struct, detailing the error correction level and the capacity for each encoding mode. - /// - /// The error correction level, which determines how much of the code can be restored if the QR code gets damaged. - /// A dictionary mapping each encoding mode to its capacity for the specific error correction level. - public VersionInfoDetails(ECCLevel errorCorrectionLevel, Dictionary capacityDict) - { - this.ErrorCorrectionLevel = errorCorrectionLevel; - this.CapacityDict = capacityDict; - } + ErrorCorrectionLevel = errorCorrectionLevel; + CapacityDict = capacityDict; + } - /// - /// Gets the error correction level of the QR code, influencing how robust the QR code is against errors and damage. - /// - public ECCLevel ErrorCorrectionLevel { get; } + /// + /// Gets the error correction level of the QR code, influencing how robust the QR code is against errors and damage. + /// + public ECCLevel ErrorCorrectionLevel { get; } - /// - /// Gets a dictionary that contains the capacities of different encoding modes under the specified error correction level. - /// These capacities dictate how many characters can be encoded under each mode. - /// - public Dictionary CapacityDict { get; } - } + /// + /// Gets a dictionary that contains the capacities of different encoding modes under the specified error correction level. + /// These capacities dictate how many characters can be encoded under each mode. + /// + public Dictionary CapacityDict { get; } } } diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index 41d3125d..5ae1db52 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -1,4 +1,4 @@ - + net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows @@ -6,12 +6,10 @@ $(DefineConstants);SYSTEM_DRAWING $(DefineConstants);NET5_0_WINDOWS $(DefineConstants);NET6_0_WINDOWS - false true - true - $(WarningsAsErrors);CS1591 - enable - 9.0 + true + $(WarningsAsErrors);CS1591 + enable diff --git a/QRCoder/SvgQRCode.cs b/QRCoder/SvgQRCode.cs index 53026dac..af668fb8 100644 --- a/QRCoder/SvgQRCode.cs +++ b/QRCoder/SvgQRCode.cs @@ -1,451 +1,414 @@ #if !NETSTANDARD1_3 -using QRCoder.Extensions; using System; -using System.Collections; -using System.Collections.Generic; using System.Drawing; using System.Text; -using System.Text.RegularExpressions; +using QRCoder.Extensions; using static QRCoder.QRCodeGenerator; using static QRCoder.SvgQRCode; -namespace QRCoder +namespace QRCoder; + +/// +/// Represents a QR code generator that outputs QR codes as SVG images. +/// +public class SvgQRCode : AbstractQRCode, IDisposable { /// - /// Represents a QR code generator that outputs QR codes as SVG images. + /// Initializes a new instance of the class. + /// Constructor without parameters to be used in COM objects connections. /// - public class SvgQRCode : AbstractQRCode, IDisposable - { - /// - /// Initializes a new instance of the class. - /// Constructor without parameters to be used in COM objects connections. - /// - public SvgQRCode() { } + public SvgQRCode() { } - /// - /// Initializes a new instance of the class with the specified . - /// - /// generated by the QRCodeGenerator. - public SvgQRCode(QRCodeData data) : base(data) { } + /// + /// Initializes a new instance of the class with the specified . + /// + /// generated by the QRCodeGenerator. + public SvgQRCode(QRCodeData data) : base(data) { } - /// - /// Returns a QR code as an SVG string. - /// - /// The pixel size each dark/light module is drawn. - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(int pixelsPerModule) - { - var viewBox = new Size(pixelsPerModule*this.QrCodeData.ModuleMatrix.Count, pixelsPerModule * this.QrCodeData.ModuleMatrix.Count); - return this.GetGraphic(viewBox, Color.Black, Color.White); - } + /// + /// Returns a QR code as an SVG string. + /// + /// The pixel size each dark/light module is drawn. + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(int pixelsPerModule) + { + var viewBox = new Size(pixelsPerModule * QrCodeData.ModuleMatrix.Count, pixelsPerModule * QrCodeData.ModuleMatrix.Count); + return GetGraphic(viewBox, Color.Black, Color.White); + } - /// - /// Returns a QR code as an SVG string with custom colors, optional quiet zones, and an optional logo. - /// - /// The pixel size each dark/light module is drawn. - /// The color of the dark modules. - /// The color of the light modules. - /// If true, a white border is drawn around the entire QR code. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) - { - var offset = drawQuietZones ? 0 : 4; - var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule); - var viewBox = new Size(edgeSize, edgeSize); - return this.GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, sizingMode, logo); - } + /// + /// Returns a QR code as an SVG string with custom colors, optional quiet zones, and an optional logo. + /// + /// The pixel size each dark/light module is drawn. + /// The color of the dark modules. + /// The color of the light modules. + /// If true, a white border is drawn around the entire QR code. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + { + var offset = drawQuietZones ? 0 : 4; + var edgeSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule); + var viewBox = new Size(edgeSize, edgeSize); + return GetGraphic(viewBox, darkColor, lightColor, drawQuietZones, sizingMode, logo); + } - /// - /// Returns a QR code as an SVG string with custom colors (in HEX syntax), optional quiet zones, and an optional logo. - /// - /// The pixel size each dark/light module is drawn. - /// The color of the dark/black modules in HEX format (e.g., #000000). - /// The color of the light/white modules in HEX format (e.g., #ffffff). - /// If true, a white border is drawn around the entire QR code. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) - { - var offset = drawQuietZones ? 0 : 4; - var edgeSize = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule); - var viewBox = new Size(edgeSize, edgeSize); - return this.GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo); - } + /// + /// Returns a QR code as an SVG string with custom colors (in HEX syntax), optional quiet zones, and an optional logo. + /// + /// The pixel size each dark/light module is drawn. + /// The color of the dark/black modules in HEX format (e.g., #000000). + /// The color of the light/white modules in HEX format (e.g., #ffffff). + /// If true, a white border is drawn around the entire QR code. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(int pixelsPerModule, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + { + var offset = drawQuietZones ? 0 : 4; + var edgeSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule - (offset * 2 * pixelsPerModule); + var viewBox = new Size(edgeSize, edgeSize); + return GetGraphic(viewBox, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo); + } - /// - /// Returns a QR code as an SVG string with optional quiet zones and an optional logo. - /// - /// The viewBox of the QR code graphic. - /// If true, a white border is drawn around the entire QR code. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(Size viewBox, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) - { - return this.GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, sizingMode, logo); - } + /// + /// Returns a QR code as an SVG string with optional quiet zones and an optional logo. + /// + /// The viewBox of the QR code graphic. + /// If true, a white border is drawn around the entire QR code. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(Size viewBox, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + => GetGraphic(viewBox, Color.Black, Color.White, drawQuietZones, sizingMode, logo); - /// - /// Returns a QR code as an SVG string with custom colors and optional quiet zones and an optional logo. - /// - /// The viewBox of the QR code graphic. - /// The color of the dark modules. - /// The color of the light modules. - /// If true, a white border is drawn around the entire QR code. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) - { - return this.GetGraphic(viewBox, ColorTranslator.ToHtml(Color.FromArgb(darkColor.ToArgb())), ColorTranslator.ToHtml(Color.FromArgb(lightColor.ToArgb())), drawQuietZones, sizingMode, logo); - } + /// + /// Returns a QR code as an SVG string with custom colors and optional quiet zones and an optional logo. + /// + /// The viewBox of the QR code graphic. + /// The color of the dark modules. + /// The color of the light modules. + /// If true, a white border is drawn around the entire QR code. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + => GetGraphic(viewBox, ColorTranslator.ToHtml(Color.FromArgb(darkColor.ToArgb())), ColorTranslator.ToHtml(Color.FromArgb(lightColor.ToArgb())), drawQuietZones, sizingMode, logo); - /// - /// Returns a QR code as an SVG string with custom colors (in HEX syntax), optional quiet zones, and an optional logo. - /// - /// The viewBox of the QR code graphic. - /// The color of the dark/black modules in HEX format (e.g., #000000). - /// The color of the light/white modules in HEX format (e.g., #ffffff). - /// If true, a white border is drawn around the entire QR code. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + /// + /// Returns a QR code as an SVG string with custom colors (in HEX syntax), optional quiet zones, and an optional logo. + /// + /// The viewBox of the QR code graphic. + /// The color of the dark/black modules in HEX format (e.g., #000000). + /// The color of the light/white modules in HEX format (e.g., #ffffff). + /// If true, a white border is drawn around the entire QR code. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public string GetGraphic(Size viewBox, string darkColorHex, string lightColorHex, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) + { + int offset = drawQuietZones ? 0 : 4; + int drawableModulesCount = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); + double pixelsPerModule = Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; + double qrSize = drawableModulesCount * pixelsPerModule; + string svgSizeAttributes = (sizingMode == SizingMode.WidthHeightAttribute) ? $@"width=""{viewBox.Width}"" height=""{viewBox.Height}""" : $@"viewBox=""0 0 {viewBox.Width} {viewBox.Height}"""; + ImageAttributes? logoAttr = null; + if (logo != null) + logoAttr = GetLogoAttributes(logo, viewBox); + + // Merge horizontal rectangles + int[,] matrix = new int[drawableModulesCount, drawableModulesCount]; + for (int yi = 0; yi < drawableModulesCount; yi += 1) { - int offset = drawQuietZones ? 0 : 4; - int drawableModulesCount = this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); - double pixelsPerModule = Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; - double qrSize = drawableModulesCount * pixelsPerModule; - string svgSizeAttributes = (sizingMode == SizingMode.WidthHeightAttribute) ? $@"width=""{viewBox.Width}"" height=""{viewBox.Height}""" : $@"viewBox=""0 0 {viewBox.Width} {viewBox.Height}"""; - ImageAttributes? logoAttr = null; - if (logo != null) - logoAttr = GetLogoAttributes(logo, viewBox); - - // Merge horizontal rectangles - int[,] matrix = new int[drawableModulesCount, drawableModulesCount]; - for (int yi = 0; yi < drawableModulesCount; yi += 1) - { - BitArray bitArray = this.QrCodeData.ModuleMatrix[yi+offset]; + var bitArray = QrCodeData.ModuleMatrix[yi + offset]; - int x0 = -1; - int xL = 0; - for (int xi = 0; xi < drawableModulesCount; xi += 1) + int x0 = -1; + int xL = 0; + for (int xi = 0; xi < drawableModulesCount; xi += 1) + { + matrix[yi, xi] = 0; + if (bitArray[xi + offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(xi * pixelsPerModule, yi * pixelsPerModule, logoAttr!.Value, pixelsPerModule))) { - matrix[yi, xi] = 0; - if (bitArray[xi+offset] && (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(xi * pixelsPerModule, yi * pixelsPerModule, logoAttr!.Value, pixelsPerModule))) + if (x0 == -1) { - if(x0 == -1) - { - x0 = xi; - } - xL += 1; + x0 = xi; } - else + xL += 1; + } + else + { + if (xL > 0) { - if(xL > 0) - { - matrix[yi, x0] = xL; - x0 = -1; - xL = 0; - } + matrix[yi, x0] = xL; + x0 = -1; + xL = 0; } } + } - if (xL > 0) - { - matrix[yi, x0] = xL; - } + if (xL > 0) + { + matrix[yi, x0] = xL; } + } - StringBuilder svgFile = new StringBuilder($@""); - svgFile.AppendLine($@""); - for (int yi = 0; yi < drawableModulesCount; yi += 1) + var svgFile = new StringBuilder($@""); + svgFile.AppendLine($@""); + for (int yi = 0; yi < drawableModulesCount; yi += 1) + { + double y = yi * pixelsPerModule; + for (int xi = 0; xi < drawableModulesCount; xi += 1) { - double y = yi * pixelsPerModule; - for (int xi = 0; xi < drawableModulesCount; xi += 1) + int xL = matrix[yi, xi]; + if (xL > 0) { - int xL = matrix[yi, xi]; - if(xL > 0) + // Merge vertical rectangles + int yL = 1; + for (int y2 = yi + 1; y2 < drawableModulesCount; y2 += 1) { - // Merge vertical rectangles - int yL = 1; - for (int y2 = yi + 1; y2 < drawableModulesCount; y2 += 1) + if (matrix[y2, xi] == xL) { - if(matrix[y2, xi] == xL) - { - matrix[y2, xi] = 0; - yL += 1; - } - else - { - break; - } + matrix[y2, xi] = 0; + yL += 1; + } + else + { + break; } - - // Output SVG rectangles - double x = xi * pixelsPerModule; - if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr!.Value, pixelsPerModule)) - svgFile.AppendLine($@""); } - } - } - //Render logo, if set - if (logo != null) - { - if (!logo.IsEmbedded()) - { - svgFile.AppendLine($@""); - svgFile.AppendLine($@""); - svgFile.AppendLine(@""); - } - else - { - var rawLogo = (string)logo.GetRawLogo(); - var svg = System.Xml.Linq.XDocument.Parse(rawLogo); - svg.Root!.SetAttributeValue("x", CleanSvgVal(logoAttr!.Value.X)); - svg.Root.SetAttributeValue("y", CleanSvgVal(logoAttr.Value.Y)); - svg.Root.SetAttributeValue("width", CleanSvgVal(logoAttr.Value.Width)); - svg.Root.SetAttributeValue("height", CleanSvgVal(logoAttr.Value.Height)); - svg.Root.SetAttributeValue("shape-rendering", "geometricPrecision"); - svgFile.AppendLine(svg.ToString(System.Xml.Linq.SaveOptions.DisableFormatting).Replace("svg:", "")); + // Output SVG rectangles + double x = xi * pixelsPerModule; + if (logo == null || !logo.FillLogoBackground() || !IsBlockedByLogo(x, y, logoAttr!.Value, pixelsPerModule)) + svgFile.AppendLine($@""); } } - - svgFile.Append(@""); - return svgFile.ToString(); - } - - private bool IsBlockedByLogo(double x, double y, ImageAttributes attr, double pixelPerModule) - { - return x + pixelPerModule >= attr.X && x <= attr.X + attr.Width && y + pixelPerModule >= attr.Y && y <= attr.Y + attr.Height; } - private ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox) + //Render logo, if set + if (logo != null) { - var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width; - var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height; - var imgPosX = viewBox.Width / 2d - imgWidth / 2d; - var imgPosY = viewBox.Height / 2d - imgHeight / 2d; - return new ImageAttributes() + if (!logo.IsEmbedded()) { - Width = imgWidth, - Height = imgHeight, - X = imgPosX, - Y = imgPosY - }; + svgFile.AppendLine($@""); + svgFile.AppendLine($@""); + svgFile.AppendLine(@""); + } + else + { + var rawLogo = (string)logo.GetRawLogo(); + var svg = System.Xml.Linq.XDocument.Parse(rawLogo); + svg.Root!.SetAttributeValue("x", CleanSvgVal(logoAttr!.Value.X)); + svg.Root.SetAttributeValue("y", CleanSvgVal(logoAttr.Value.Y)); + svg.Root.SetAttributeValue("width", CleanSvgVal(logoAttr.Value.Width)); + svg.Root.SetAttributeValue("height", CleanSvgVal(logoAttr.Value.Height)); + svg.Root.SetAttributeValue("shape-rendering", "geometricPrecision"); + svgFile.AppendLine(svg.ToString(System.Xml.Linq.SaveOptions.DisableFormatting).Replace("svg:", "")); + } } - private struct ImageAttributes - { - public double Width; - public double Height; - public double X; - public double Y; - } + svgFile.Append(@""); + return svgFile.ToString(); + } - private string CleanSvgVal(double input) - { - //Clean double values for international use/formats - //We use explicitly "G15" to avoid differences between .NET full and Core platforms - //https://stackoverflow.com/questions/64898117/tostring-has-a-different-behavior-between-net-462-and-net-core-3-1 - return input.ToString("G15", System.Globalization.CultureInfo.InvariantCulture); - } + private bool IsBlockedByLogo(double x, double y, ImageAttributes attr, double pixelPerModule) + => x + pixelPerModule >= attr.X && x <= attr.X + attr.Width && y + pixelPerModule >= attr.Y && y <= attr.Y + attr.Height; - /// - /// Mode of sizing attribution on svg root node - /// - public enum SizingMode + private ImageAttributes GetLogoAttributes(SvgLogo logo, Size viewBox) + { + var imgWidth = logo.GetIconSizePercent() / 100d * viewBox.Width; + var imgHeight = logo.GetIconSizePercent() / 100d * viewBox.Height; + var imgPosX = viewBox.Width / 2d - imgWidth / 2d; + var imgPosY = viewBox.Height / 2d - imgHeight / 2d; + return new ImageAttributes() { - WidthHeightAttribute, - ViewBoxAttribute - } + Width = imgWidth, + Height = imgHeight, + X = imgPosX, + Y = imgPosY + }; + } - /// - /// Represents a logo graphic that can be rendered on a SvgQRCode - /// - public class SvgLogo - { - private string _logoData; - private MediaType _mediaType; - private int _iconSizePercent; - private bool _fillLogoBackground; - private object _logoRaw; - private bool _isEmbedded; + private struct ImageAttributes + { + public double Width; + public double Height; + public double X; + public double Y; + } + + //Clean double values for international use/formats + //We use explicitly "G15" to avoid differences between .NET full and Core platforms + //https://stackoverflow.com/questions/64898117/tostring-has-a-different-behavior-between-net-462-and-net-core-3-1 + private string CleanSvgVal(double input) + => input.ToString("G15", System.Globalization.CultureInfo.InvariantCulture); + + /// + /// Mode of sizing attribution on svg root node + /// + public enum SizingMode + { + WidthHeightAttribute, + ViewBoxAttribute + } + + /// + /// Represents a logo graphic that can be rendered on a SvgQRCode + /// + public class SvgLogo + { + private readonly string _logoData; + private readonly MediaType _mediaType; + private readonly int _iconSizePercent; + private readonly bool _fillLogoBackground; + private readonly object _logoRaw; + private readonly bool _isEmbedded; #if SYSTEM_DRAWING - /// - /// Create a logo object to be used in SvgQRCode renderer - /// - /// Logo to be rendered as Bitmap/rasterized graphic - /// Degree of percentage coverage of the QR code by the logo - /// If true, the background behind the logo will be cleaned + /// + /// Create a logo object to be used in SvgQRCode renderer + /// + /// Logo to be rendered as Bitmap/rasterized graphic + /// Degree of percentage coverage of the QR code by the logo + /// If true, the background behind the logo will be cleaned #if NET6_0_OR_GREATER - [System.Runtime.Versioning.SupportedOSPlatform("windows")] + [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public SvgLogo(Bitmap iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true) + public SvgLogo(Bitmap iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true) + { + _iconSizePercent = iconSizePercent; + using (var ms = new System.IO.MemoryStream()) { - _iconSizePercent = iconSizePercent; - using (var ms = new System.IO.MemoryStream()) - { - using (var bitmap = new Bitmap(iconRasterized)) - { - bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); - _logoData = Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length, Base64FormattingOptions.None); - } - } - _mediaType = MediaType.PNG; - _fillLogoBackground = fillLogoBackground; - _logoRaw = iconRasterized; - _isEmbedded = false; + using var bitmap = new Bitmap(iconRasterized); + bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png); + _logoData = Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length, Base64FormattingOptions.None); } + _mediaType = MediaType.PNG; + _fillLogoBackground = fillLogoBackground; + _logoRaw = iconRasterized; + _isEmbedded = false; + } #endif - /// - /// Create a logo object to be used in SvgQRCode renderer - /// - /// Logo to be rendered as SVG/vectorized graphic/string - /// Degree of percentage coverage of the QR code by the logo - /// If true, the background behind the logo will be cleaned - /// If true, the logo will embedded as native svg instead of embedding it as image-tag - public SvgLogo(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true, bool iconEmbedded = true) - { - _iconSizePercent = iconSizePercent; - _logoData = Convert.ToBase64String(Encoding.UTF8.GetBytes(iconVectorized), Base64FormattingOptions.None); - _mediaType = MediaType.SVG; - _fillLogoBackground = fillLogoBackground; - _logoRaw = iconVectorized; - _isEmbedded = iconEmbedded; - } + /// + /// Create a logo object to be used in SvgQRCode renderer + /// + /// Logo to be rendered as SVG/vectorized graphic/string + /// Degree of percentage coverage of the QR code by the logo + /// If true, the background behind the logo will be cleaned + /// If true, the logo will embedded as native svg instead of embedding it as image-tag + public SvgLogo(string iconVectorized, int iconSizePercent = 15, bool fillLogoBackground = true, bool iconEmbedded = true) + { + _iconSizePercent = iconSizePercent; + _logoData = Convert.ToBase64String(Encoding.UTF8.GetBytes(iconVectorized), Base64FormattingOptions.None); + _mediaType = MediaType.SVG; + _fillLogoBackground = fillLogoBackground; + _logoRaw = iconVectorized; + _isEmbedded = iconEmbedded; + } - /// - /// Create a logo object to be used in SvgQRCode renderer - /// - /// Logo to be rendered as PNG - /// Degree of percentage coverage of the QR code by the logo - /// If true, the background behind the logo will be cleaned - public SvgLogo(byte[] iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true) - { - _iconSizePercent = iconSizePercent; - _logoData = Convert.ToBase64String(iconRasterized, Base64FormattingOptions.None); - _mediaType = MediaType.PNG; - _fillLogoBackground = fillLogoBackground; - _logoRaw = iconRasterized; - _isEmbedded = false; - } + /// + /// Create a logo object to be used in SvgQRCode renderer + /// + /// Logo to be rendered as PNG + /// Degree of percentage coverage of the QR code by the logo + /// If true, the background behind the logo will be cleaned + public SvgLogo(byte[] iconRasterized, int iconSizePercent = 15, bool fillLogoBackground = true) + { + _iconSizePercent = iconSizePercent; + _logoData = Convert.ToBase64String(iconRasterized, Base64FormattingOptions.None); + _mediaType = MediaType.PNG; + _fillLogoBackground = fillLogoBackground; + _logoRaw = iconRasterized; + _isEmbedded = false; + } - /// - /// Returns the raw logo's data - /// - public object GetRawLogo() - { - return _logoRaw; - } + /// + /// Returns the raw logo's data + /// + public object GetRawLogo() => _logoRaw; - /// - /// Defines, if the logo shall be natively embedded. - /// true=native svg embedding, false=embedding via image-tag - /// - public bool IsEmbedded() - { - return _isEmbedded; - } + /// + /// Defines, if the logo shall be natively embedded. + /// true=native svg embedding, false=embedding via image-tag + /// + public bool IsEmbedded() => _isEmbedded; - /// - /// Returns the media type of the logo - /// - /// - public MediaType GetMediaType() - { - return _mediaType; - } + /// + /// Returns the media type of the logo + /// + /// + public MediaType GetMediaType() => _mediaType; - /// - /// Returns the logo as data-uri - /// - public string GetDataUri() - { - return $"data:{GetMimeType(_mediaType)};base64,{_logoData}"; - } + /// + /// Returns the logo as data-uri + /// + public string GetDataUri() + => $"data:{GetMimeType(_mediaType)};base64,{_logoData}"; - /// - /// Returns how much of the QR code should be covered by the logo (in percent) - /// - public int GetIconSizePercent() - { - return _iconSizePercent; - } + /// + /// Returns how much of the QR code should be covered by the logo (in percent) + /// + public int GetIconSizePercent() => _iconSizePercent; - /// - /// Returns if the background of the logo should be cleaned (no QR modules will be rendered behind the logo) - /// - public bool FillLogoBackground() - { - return _fillLogoBackground; - } + /// + /// Returns if the background of the logo should be cleaned (no QR modules will be rendered behind the logo) + /// + public bool FillLogoBackground() => _fillLogoBackground; - /// - /// Media types for SvgLogos - /// - public enum MediaType : int - { + /// + /// Media types for SvgLogos + /// + public enum MediaType : int + { #pragma warning disable CS0618 // Type or member is obsolete - [StringValue("image/png")] + [StringValue("image/png")] #pragma warning restore CS0618 // Type or member is obsolete - PNG = 0, + PNG = 0, #pragma warning disable CS0618 // Type or member is obsolete - [StringValue("image/svg+xml")] + [StringValue("image/svg+xml")] #pragma warning restore CS0618 // Type or member is obsolete - SVG = 1 - } - - private string GetMimeType(MediaType type) - { - switch (type) - { - case MediaType.PNG: - return "image/png"; - case MediaType.SVG: - return "image/svg+xml"; - default: - throw new ArgumentOutOfRangeException(nameof(type)); - } - } - + SVG = 1 } + + private string GetMimeType(MediaType type) => type switch + { + MediaType.PNG => "image/png", + MediaType.SVG => "image/svg+xml", + _ => throw new ArgumentOutOfRangeException(nameof(type)), + }; + } +} +/// +/// Provides static methods for creating SVG QR codes. +/// +public static class SvgQRCodeHelper +{ /// - /// Provides static methods for creating SVG QR codes. + /// Creates an SVG QR code with a single function call. /// - public static class SvgQRCodeHelper + /// The text or payload to be encoded inside the QR code. + /// The pixel size each dark/light module of the QR code will occupy in the final QR code image. + /// The color of the dark modules in HEX format (e.g., #000000). + /// The color of the light modules in HEX format (e.g., #ffffff). + /// The level of error correction data. + /// Specifies whether the generator should be forced to work in UTF-8 mode. + /// Specifies whether the byte-order-mark should be used. + /// Specifies which ECI mode should be used. + /// Sets the fixed QR code target version. + /// Indicates if quiet zones around the QR code should be drawn. + /// Defines whether width/height or viewBox should be used for size definition. + /// An optional logo to be rendered on the code (either Bitmap or SVG). + /// Returns the QR code graphic as an SVG string. + public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) { - /// - /// Creates an SVG QR code with a single function call. - /// - /// The text or payload to be encoded inside the QR code. - /// The pixel size each dark/light module of the QR code will occupy in the final QR code image. - /// The color of the dark modules in HEX format (e.g., #000000). - /// The color of the light modules in HEX format (e.g., #ffffff). - /// The level of error correction data. - /// Specifies whether the generator should be forced to work in UTF-8 mode. - /// Specifies whether the byte-order-mark should be used. - /// Specifies which ECI mode should be used. - /// Sets the fixed QR code target version. - /// Indicates if quiet zones around the QR code should be drawn. - /// Defines whether width/height or viewBox should be used for size definition. - /// An optional logo to be rendered on the code (either Bitmap or SVG). - /// Returns the QR code graphic as an SVG string. - public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHex, string lightColorHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, SizingMode sizingMode = SizingMode.WidthHeightAttribute, SvgLogo? logo = null) - { - using (var qrGenerator = new QRCodeGenerator()) - using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion)) - using (var qrCode = new SvgQRCode(qrCodeData)) - return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo); - } + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion); + using var qrCode = new SvgQRCode(qrCodeData); + return qrCode.GetGraphic(pixelsPerModule, darkColorHex, lightColorHex, drawQuietZones, sizingMode, logo); } } diff --git a/QRCoderApiTests/ApiApprovalTests.cs b/QRCoderApiTests/ApiApprovalTests.cs index b64b7836..aeb7d316 100644 --- a/QRCoderApiTests/ApiApprovalTests.cs +++ b/QRCoderApiTests/ApiApprovalTests.cs @@ -1,11 +1,11 @@ -using PublicApiGenerator; -using Shouldly; using System; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Xml.Linq; +using PublicApiGenerator; +using Shouldly; using Xunit; /********************************************* @@ -36,80 +36,79 @@ * *********************************************/ -namespace QRCoderApiTests +namespace QRCoderApiTests; + +/// +/// See more info about API approval tests here . +/// +public class ApiApprovalTests { - /// - /// See more info about API approval tests here . - /// - public class ApiApprovalTests + [Theory] + [InlineData(typeof(QRCoder.QRCodeData))] + [InlineData(typeof(QRCoder.Xaml.XamlQRCode))] + public void PublicApi(Type type) { - [Theory] - [InlineData(typeof(QRCoder.QRCodeData))] - [InlineData(typeof(QRCoder.Xaml.XamlQRCode))] - public void PublicApi(Type type) - { - string baseDir = AppDomain.CurrentDomain.BaseDirectory; - string projectName = type.Assembly.GetName().Name!; - string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}.."); - string projectDir = Path.Combine(testDir, ".."); - string buildDir = Path.Combine(projectDir, projectName, "bin", + string baseDir = AppDomain.CurrentDomain.BaseDirectory; + string projectName = type.Assembly.GetName().Name!; + string testDir = Path.Combine(baseDir, $"..{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}.."); + string projectDir = Path.Combine(testDir, ".."); + string buildDir = Path.Combine(projectDir, projectName, "bin", #if DEBUG - "Debug"); + "Debug"); #else - "Release"); + "Release"); #endif - Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist"); - string csProject = Path.Combine(projectDir, projectName, projectName + ".csproj"); - var project = XDocument.Load(csProject); - string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries); + Debug.Assert(Directory.Exists(buildDir), $"Directory '{buildDir}' doesn't exist"); + string csProject = Path.Combine(projectDir, projectName, projectName + ".csproj"); + var project = XDocument.Load(csProject); + string[] tfms = project.Descendants("TargetFrameworks").Union(project.Descendants("TargetFramework")).First().Value.Split(";", StringSplitOptions.RemoveEmptyEntries); - // There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out - string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray(); - Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}"); + // There may be old stuff from earlier builds like net45, netcoreapp3.0, etc. so filter it out + string[] actualTfmDirs = Directory.GetDirectories(buildDir).Where(dir => tfms.Any(tfm => dir.EndsWith(tfm))).ToArray(); + Debug.Assert(actualTfmDirs.Length > 0, $"Directory '{buildDir}' doesn't contain subdirectories matching {string.Join(";", tfms)}"); - (string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions - { - IncludeAssemblyAttributes = false, - //AllowNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" }, - ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" } - }) + Environment.NewLine)).ToArray(); + (string tfm, string content)[] publicApi = actualTfmDirs.Select(tfmDir => (new DirectoryInfo(tfmDir).Name.Replace(".", ""), Assembly.LoadFile(Path.Combine(tfmDir, projectName + ".dll")).GeneratePublicApi(new ApiGeneratorOptions + { + IncludeAssemblyAttributes = false, + //AllowNamespacePrefixes = new[] { "Microsoft.Extensions.DependencyInjection" }, + ExcludeAttributes = new[] { "System.Diagnostics.DebuggerDisplayAttribute", "System.Diagnostics.CodeAnalysis.AllowNullAttribute" } + }) + Environment.NewLine)).ToArray(); - if (publicApi.DistinctBy(item => item.content).Count() == 1) + if (publicApi.DistinctBy(item => item.content).Count() == 1) + { + AutoApproveOrFail(publicApi[0].content, ""); + } + else + { + foreach (var item in publicApi.ToLookup(item => item.content)) { - AutoApproveOrFail(publicApi[0].content, ""); + AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x))); } - else + } + + // Approval test should (re)generate approved.txt files locally if needed. + // Approval test should fail on CI. + // https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables + void AutoApproveOrFail(string publicApi, string folder) + { + string file = null!; + + try { - foreach (var item in publicApi.ToLookup(item => item.content)) - { - AutoApproveOrFail(item.Key, string.Join("+", item.Select(x => x.tfm).OrderBy(x => x))); - } + publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}")); } - - // Approval test should (re)generate approved.txt files locally if needed. - // Approval test should fail on CI. - // https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables - void AutoApproveOrFail(string publicApi, string folder) + catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null) { - string file = null!; - - try + string? received = Path.Combine(testDir, folder, file); + string? approved = received.Replace(".received.txt", ".approved.txt"); + if (File.Exists(received) && File.Exists(approved)) { - publicApi.ShouldMatchApproved(options => options.SubFolder(folder).NoDiff().WithFilenameGenerator((testMethodInfo, discriminator, fileType, fileExtension) => file = $"{type.Assembly.GetName().Name}.{fileType}.{fileExtension}")); + File.Copy(received, approved, overwrite: true); + File.Delete(received); } - catch (ShouldMatchApprovedException) when (Environment.GetEnvironmentVariable("CI") == null) + else { - string? received = Path.Combine(testDir, folder, file); - string? approved = received.Replace(".received.txt", ".approved.txt"); - if (File.Exists(received) && File.Exists(approved)) - { - File.Copy(received, approved, overwrite: true); - File.Delete(received); - } - else - { - throw; - } + throw; } } } diff --git a/QRCoderApiTests/QRCoderApiTests.csproj b/QRCoderApiTests/QRCoderApiTests.csproj index 6214ca37..23daa646 100644 --- a/QRCoderApiTests/QRCoderApiTests.csproj +++ b/QRCoderApiTests/QRCoderApiTests.csproj @@ -1,4 +1,4 @@ - + net6.0-windows @@ -18,5 +18,5 @@ - + diff --git a/QRCoderBenchmarks/Program.cs b/QRCoderBenchmarks/Program.cs index 2daaef53..66129296 100644 --- a/QRCoderBenchmarks/Program.cs +++ b/QRCoderBenchmarks/Program.cs @@ -1,3 +1,3 @@ -using BenchmarkDotNet.Running; +using BenchmarkDotNet.Running; BenchmarkRunner.Run(typeof(Program).Assembly); diff --git a/QRCoderBenchmarks/QRCodeGenerator.cs b/QRCoderBenchmarks/QRCodeGenerator.cs index 742d5be5..c8256adb 100644 --- a/QRCoderBenchmarks/QRCodeGenerator.cs +++ b/QRCoderBenchmarks/QRCodeGenerator.cs @@ -1,4 +1,4 @@ -using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Attributes; namespace QRCoderBenchmarks; @@ -9,7 +9,7 @@ public class QRCodeGenerator public void CreateQRCode() { var payload = new QRCoder.PayloadGenerator.Url("HTTP://WWW.GOOGLE.COM/"); - QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); + var qrGenerator = new QRCoder.QRCodeGenerator(); _ = qrGenerator.CreateQrCode(payload, QRCoder.QRCodeGenerator.ECCLevel.L); } @@ -17,7 +17,7 @@ public void CreateQRCode() public void CreateQRCodeLong() { var payload = new QRCoder.PayloadGenerator.Url("https://github.com/codebude/QRCoder/blob/f89aa90081f369983a9ba114e49cc6ebf0b2a7b1/QRCoder/Framework4.0Methods/Stream4Methods.cs"); - QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); + var qrGenerator = new QRCoder.QRCodeGenerator(); _ = qrGenerator.CreateQrCode(payload, QRCoder.QRCodeGenerator.ECCLevel.H); } @@ -25,7 +25,7 @@ public void CreateQRCodeLong() public void CreateQRCodeLongest() { var str = new string('a', 2600); - QRCoder.QRCodeGenerator qrGenerator = new QRCoder.QRCodeGenerator(); + var qrGenerator = new QRCoder.QRCodeGenerator(); _ = qrGenerator.CreateQrCode(str, QRCoder.QRCodeGenerator.ECCLevel.L); } } diff --git a/QRCoderBenchmarks/QRCoderBenchmarks.csproj b/QRCoderBenchmarks/QRCoderBenchmarks.csproj index 3eb77990..323afbdb 100644 --- a/QRCoderBenchmarks/QRCoderBenchmarks.csproj +++ b/QRCoderBenchmarks/QRCoderBenchmarks.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/QRCoderConsole/DataObjects/SupportedImageFormat.cs b/QRCoderConsole/DataObjects/SupportedImageFormat.cs index 821cfd6b..5ec0c5b4 100644 --- a/QRCoderConsole/DataObjects/SupportedImageFormat.cs +++ b/QRCoderConsole/DataObjects/SupportedImageFormat.cs @@ -1,17 +1,16 @@ -namespace QRCoderConsole.DataObjects +namespace QRCoderConsole.DataObjects; + +public enum SupportedImageFormat { - public enum SupportedImageFormat - { - Png, - Jpg, - Gif, - Bmp, - Tiff, - Svg, + Png, + Jpg, + Gif, + Bmp, + Tiff, + Svg, #if !NET5_0 || NET5_0_WINDOWS - Xaml, + Xaml, #endif - Ps, - Eps, - } + Ps, + Eps, } diff --git a/QRCoderConsole/Program.cs b/QRCoderConsole/Program.cs index e9af916a..f15a7060 100644 --- a/QRCoderConsole/Program.cs +++ b/QRCoderConsole/Program.cs @@ -1,278 +1,251 @@ -using System; +using System; using System.Drawing.Imaging; using System.IO; -using NDesk.Options; -using QRCoderConsole.DataObjects; -using QRCoder; using System.Text; using System.Windows.Markup; +using NDesk.Options; +using QRCoder; +using QRCoderConsole.DataObjects; + +namespace QRCoderConsole; -namespace QRCoderConsole -{ #if NET6_0_WINDOWS - [System.Runtime.Versioning.SupportedOSPlatform("windows")] +[System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - class MainClass +internal class MainClass +{ + public static void Main(string[] args) { - public static void Main (string[] args) - { - var friendlyName = AppDomain.CurrentDomain.FriendlyName; - var newLine = Environment.NewLine; - var setter = new OptionSetter (); + var friendlyName = AppDomain.CurrentDomain.FriendlyName; + var newLine = Environment.NewLine; + var setter = new OptionSetter(); - String fileName = null, outputFileName = null, payload = null; + string fileName = null, outputFileName = null, payload = null; - QRCodeGenerator.ECCLevel eccLevel = QRCodeGenerator.ECCLevel.L; - SupportedImageFormat imageFormat = SupportedImageFormat.Png; + var eccLevel = QRCodeGenerator.ECCLevel.L; + var imageFormat = SupportedImageFormat.Png; - int pixelsPerModule = 20; - string foregroundColor = "#000000"; - string backgroundColor = "#FFFFFF"; + int pixelsPerModule = 20; + string foregroundColor = "#000000"; + string backgroundColor = "#FFFFFF"; - var showHelp = false; + var showHelp = false; - var optionSet = new OptionSet { - { "e|ecc-level=", - "error correction level", - value => eccLevel = setter.GetECCLevel(value) - }, - { "f|output-format=", - $"Image format for outputfile. Possible values: {string.Join(", ", Enum.GetNames(typeof(SupportedImageFormat)))} (default: png)", - value => { Enum.TryParse(value, true, out imageFormat); } - }, - { - "i|in=", - "input file | alternative to parameter -p", - value => fileName = value - }, - { - "p|payload=", - "payload string | alternative to parameter -i", - value => payload = value - }, - { - "o|out=", - "output file", - value => outputFileName = value - }, - { "s|pixel=", - "pixels per module", - value => { - if (int.TryParse(value, out pixelsPerModule)) - { - if (pixelsPerModule < 1) - { - pixelsPerModule = 20; - } - } - else + var optionSet = new OptionSet { + { "e|ecc-level=", + "error correction level", + value => eccLevel = setter.GetECCLevel(value) + }, + { "f|output-format=", + $"Image format for outputfile. Possible values: {string.Join(", ", Enum.GetNames(typeof(SupportedImageFormat)))} (default: png)", + value => Enum.TryParse(value, true, out imageFormat) + }, + { + "i|in=", + "input file | alternative to parameter -p", + value => fileName = value + }, + { + "p|payload=", + "payload string | alternative to parameter -i", + value => payload = value + }, + { + "o|out=", + "output file", + value => outputFileName = value + }, + { "s|pixel=", + "pixels per module", + value => { + if (int.TryParse(value, out pixelsPerModule)) + { + if (pixelsPerModule < 1) { pixelsPerModule = 20; } - } - }, - { "l|background=", - "background color", - value => backgroundColor = value - }, - { "d|foreground=", - "foreground color", - value => foregroundColor = value - }, - { "h|help", - "show this message and exit.", - value => showHelp = value != null + } + else + { + pixelsPerModule = 20; + } } + }, + { "l|background=", + "background color", + value => backgroundColor = value + }, + { "d|foreground=", + "foreground color", + value => foregroundColor = value + }, + { "h|help", + "show this message and exit.", + value => showHelp = value != null + } - }; + }; - try - { + try + { - optionSet.Parse(args); + optionSet.Parse(args); - if (showHelp) - { - ShowHelp(optionSet); - } + if (showHelp) + { + ShowHelp(optionSet); + } - string text = null; - if (fileName != null) - { - var fileInfo = new FileInfo(fileName); - if (fileInfo.Exists) - { - text = GetTextFromFile(fileInfo); - } - else - { - Console.WriteLine($"{friendlyName}: {fileName}: No such file or directory"); - } - } - else if (payload != null) + string text = null; + if (fileName != null) + { + var fileInfo = new FileInfo(fileName); + if (fileInfo.Exists) { - text = payload; + text = GetTextFromFile(fileInfo); } else { - var stdin = Console.OpenStandardInput(); - - text = GetTextFromStream(stdin); + Console.WriteLine($"{friendlyName}: {fileName}: No such file or directory"); } + } + else if (payload != null) + { + text = payload; + } + else + { + var stdin = Console.OpenStandardInput(); - if (text != null) - { - GenerateQRCode(text, eccLevel, outputFileName, imageFormat, pixelsPerModule, foregroundColor, backgroundColor); - } + text = GetTextFromStream(stdin); } - catch (Exception oe) + + if (text != null) { - Console.Error.WriteLine( - $"{friendlyName}:{newLine}{oe.GetType().FullName}{newLine}{oe.Message}{newLine}{oe.StackTrace}{newLine}Try '{friendlyName} --help' for more information"); - Environment.Exit(-1); + GenerateQRCode(text, eccLevel, outputFileName, imageFormat, pixelsPerModule, foregroundColor, backgroundColor); } } + catch (Exception oe) + { + Console.Error.WriteLine( + $"{friendlyName}:{newLine}{oe.GetType().FullName}{newLine}{oe.Message}{newLine}{oe.StackTrace}{newLine}Try '{friendlyName} --help' for more information"); + Environment.Exit(-1); + } + } - private static void GenerateQRCode(string payloadString, QRCodeGenerator.ECCLevel eccLevel, string outputFileName, SupportedImageFormat imgFormat, int pixelsPerModule, string foreground, string background) + private static void GenerateQRCode(string payloadString, QRCodeGenerator.ECCLevel eccLevel, string outputFileName, SupportedImageFormat imgFormat, int pixelsPerModule, string foreground, string background) + { + using var generator = new QRCodeGenerator(); + using var data = generator.CreateQrCode(payloadString, eccLevel); + switch (imgFormat) { - using (var generator = new QRCodeGenerator()) - { - using (var data = generator.CreateQrCode(payloadString, eccLevel)) + case SupportedImageFormat.Png: + case SupportedImageFormat.Jpg: + case SupportedImageFormat.Gif: + case SupportedImageFormat.Bmp: + case SupportedImageFormat.Tiff: + using (var code = new QRCode(data)) + { + using var bitmap = code.GetGraphic(pixelsPerModule, foreground, background, true); + var actualFormat = new OptionSetter().GetImageFormat(imgFormat.ToString()); + bitmap.Save(outputFileName, actualFormat); + } + break; + case SupportedImageFormat.Svg: + using (var code = new SvgQRCode(data)) { - switch (imgFormat) - { - case SupportedImageFormat.Png: - case SupportedImageFormat.Jpg: - case SupportedImageFormat.Gif: - case SupportedImageFormat.Bmp: - case SupportedImageFormat.Tiff: - using (var code = new QRCode(data)) - { - using (var bitmap = code.GetGraphic(pixelsPerModule, foreground, background, true)) - { - var actualFormat = new OptionSetter().GetImageFormat(imgFormat.ToString()); - bitmap.Save(outputFileName, actualFormat); - } - } - break; - case SupportedImageFormat.Svg: - using (var code = new SvgQRCode(data)) - { - var test = code.GetGraphic(pixelsPerModule, foreground, background, true); - using (var f = File.CreateText(outputFileName)) - { - f.Write(test); - f.Flush(); - } - } - break; + var test = code.GetGraphic(pixelsPerModule, foreground, background, true); + using var f = File.CreateText(outputFileName); + f.Write(test); + f.Flush(); + } + break; #if NETFRAMEWORK || NET5_0_WINDOWS || NET6_0_WINDOWS - case SupportedImageFormat.Xaml: - using (var code = new QRCoder.Xaml.XamlQRCode(data)) - { - var test = XamlWriter.Save(code.GetGraphic(pixelsPerModule, foreground, background, true)); - using (var f = File.CreateText(outputFileName)) - { - f.Write(test); - f.Flush(); - } - } - break; + case SupportedImageFormat.Xaml: + using (var code = new QRCoder.Xaml.XamlQRCode(data)) + { + var test = XamlWriter.Save(code.GetGraphic(pixelsPerModule, foreground, background, true)); + using var f = File.CreateText(outputFileName); + f.Write(test); + f.Flush(); + } + break; #endif - case SupportedImageFormat.Ps: - case SupportedImageFormat.Eps: - using (var code = new PostscriptQRCode(data)) - { - var test = code.GetGraphic(pixelsPerModule, foreground, background, true, - imgFormat == SupportedImageFormat.Eps); - using (var f = File.CreateText(outputFileName)) - { - f.Write(test); - f.Flush(); - } - } - break; - default: - throw new ArgumentOutOfRangeException(nameof(imgFormat), imgFormat, null); - } - + case SupportedImageFormat.Ps: + case SupportedImageFormat.Eps: + using (var code = new PostscriptQRCode(data)) + { + var test = code.GetGraphic(pixelsPerModule, foreground, background, true, + imgFormat == SupportedImageFormat.Eps); + using var f = File.CreateText(outputFileName); + f.Write(test); + f.Flush(); } - } + break; + default: + throw new ArgumentOutOfRangeException(nameof(imgFormat), imgFormat, null); } + } - private static string GetTextFromFile(FileInfo fileInfo) + private static string GetTextFromFile(FileInfo fileInfo) + { + var buffer = new byte[fileInfo.Length]; + + using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open)) { - var buffer = new byte[fileInfo.Length]; + fileStream.Read(buffer, 0, buffer.Length); + } - using (var fileStream = new FileStream(fileInfo.FullName, FileMode.Open)) - { - fileStream.Read(buffer, 0, buffer.Length); - } + return Encoding.UTF8.GetString(buffer); + } - return Encoding.UTF8.GetString(buffer); - } + private static string GetTextFromStream(Stream stream) + { + var buffer = new byte[256]; + var bytesRead = 0; - private static string GetTextFromStream(Stream stream) + using var memoryStream = new MemoryStream(); + while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) { - var buffer = new byte[256]; - var bytesRead = 0; - - using (var memoryStream = new MemoryStream()) - { - while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0) - { - memoryStream.Write(buffer, 0, bytesRead); - } + memoryStream.Write(buffer, 0, bytesRead); + } - var text = Encoding.UTF8.GetString(memoryStream.ToArray()); + var text = Encoding.UTF8.GetString(memoryStream.ToArray()); - Console.WriteLine($"text retrieved from input stream: {text}"); + Console.WriteLine($"text retrieved from input stream: {text}"); - return text; - } - } + return text; + } - private static void ShowHelp(OptionSet optionSet) - { - optionSet.WriteOptionDescriptions(Console.Out); - Environment.Exit(0); - } + private static void ShowHelp(OptionSet optionSet) + { + optionSet.WriteOptionDescriptions(Console.Out); + Environment.Exit(0); } +} - public class OptionSetter +public class OptionSetter +{ + public QRCodeGenerator.ECCLevel GetECCLevel(string value) { - public QRCodeGenerator.ECCLevel GetECCLevel(string value) - { - QRCodeGenerator.ECCLevel level; - Enum.TryParse(value, out level); + Enum.TryParse(value, out QRCodeGenerator.ECCLevel level); - return level; - } + return level; + } #if NET6_0_WINDOWS [System.Runtime.Versioning.SupportedOSPlatform("windows")] #endif - public ImageFormat GetImageFormat(string value) - { - switch (value.ToLower()) - { - case "jpg": - return ImageFormat.Jpeg; - case "jpeg": - return ImageFormat.Jpeg; - case "gif": - return ImageFormat.Gif; - case "bmp": - return ImageFormat.Bmp; - case "tiff": - return ImageFormat.Tiff; - case "png": - default: - return ImageFormat.Png; - } - } - } + public ImageFormat GetImageFormat(string value) => value.ToLower() switch + { + "jpg" => ImageFormat.Jpeg, + "jpeg" => ImageFormat.Jpeg, + "gif" => ImageFormat.Gif, + "bmp" => ImageFormat.Bmp, + "tiff" => ImageFormat.Tiff, + _ => ImageFormat.Png, + }; } diff --git a/QRCoderConsole/Properties/AssemblyInfo.cs b/QRCoderConsole/Properties/AssemblyInfo.cs index ad04bef2..9c3c34af 100644 --- a/QRCoderConsole/Properties/AssemblyInfo.cs +++ b/QRCoderConsole/Properties/AssemblyInfo.cs @@ -1,23 +1,23 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; // Information about this assembly is defined by the following attributes. // Change them to the values specific to your project. -[assembly: AssemblyTitle ("QRCoderConsole")] -[assembly: AssemblyDescription ("")] -[assembly: AssemblyConfiguration ("")] -[assembly: AssemblyCompany ("")] -[assembly: AssemblyProduct ("")] -[assembly: AssemblyCopyright ("mishochu, codebude and others")] -[assembly: AssemblyTrademark ("")] -[assembly: AssemblyCulture ("")] +[assembly: AssemblyTitle("QRCoderConsole")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("mishochu, codebude and others")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". // The form "{Major}.{Minor}.*" will automatically update the build and revision, // and "{Major}.{Minor}.{Build}.*" will update just the revision. -[assembly: AssemblyVersion ("1.0.0.1")] +[assembly: AssemblyVersion("1.0.0.1")] // The following attributes are used to specify the signing key for the assembly, // if desired. See the Mono documentation for more information about signing. diff --git a/QRCoderConsole/QRCoderConsole.csproj b/QRCoderConsole/QRCoderConsole.csproj index 94c07191..e30344de 100644 --- a/QRCoderConsole/QRCoderConsole.csproj +++ b/QRCoderConsole/QRCoderConsole.csproj @@ -1,11 +1,11 @@ - + net45;net5.0;net5.0-windows;net6.0-windows true $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS true Exe false @@ -32,9 +32,9 @@ - + - + \ No newline at end of file diff --git a/QRCoderDemo/Form1.Designer.cs b/QRCoderDemo/Form1.Designer.cs index c4c06dfe..2dea5c8c 100644 --- a/QRCoderDemo/Form1.Designer.cs +++ b/QRCoderDemo/Form1.Designer.cs @@ -1,261 +1,260 @@ -namespace QRCoderDemo +namespace QRCoderDemo; + +partial class Form1 { - partial class Form1 - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); + components.Dispose(); } + base.Dispose(disposing); + } - #region Vom Windows Form-Designer generierter Code + #region Vom Windows Form-Designer generierter Code - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); - this.buttonGenerate = new System.Windows.Forms.Button(); - this.textBoxQRCode = new System.Windows.Forms.TextBox(); - this.pictureBoxQRCode = new System.Windows.Forms.PictureBox(); - this.comboBoxECC = new System.Windows.Forms.ComboBox(); - this.labelECC = new System.Windows.Forms.Label(); - this.labelIcon = new System.Windows.Forms.Label(); - this.iconPath = new System.Windows.Forms.TextBox(); - this.selectIconBtn = new System.Windows.Forms.Button(); - this.labelIconsize = new System.Windows.Forms.Label(); - this.iconSize = new System.Windows.Forms.NumericUpDown(); - this.buttonSave = new System.Windows.Forms.Button(); - this.colorDialogPrimaryColor = new System.Windows.Forms.ColorDialog(); - this.labelPreviewPrimaryColor = new System.Windows.Forms.Label(); - this.panelPreviewPrimaryColor = new System.Windows.Forms.Panel(); - this.labelPreviewBackgroundColor = new System.Windows.Forms.Label(); - this.panelPreviewBackgroundColor = new System.Windows.Forms.Panel(); - this.colorDialogBackgroundColor = new System.Windows.Forms.ColorDialog(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQRCode)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.iconSize)).BeginInit(); - this.SuspendLayout(); - // - // buttonGenerate - // - this.buttonGenerate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.buttonGenerate.Location = new System.Drawing.Point(440, 23); - this.buttonGenerate.Name = "buttonGenerate"; - this.buttonGenerate.Size = new System.Drawing.Size(62, 23); - this.buttonGenerate.TabIndex = 0; - this.buttonGenerate.Text = "Generate"; - this.buttonGenerate.UseVisualStyleBackColor = true; - this.buttonGenerate.Click += new System.EventHandler(this.buttonGenerate_Click); - // - // textBoxQRCode - // - this.textBoxQRCode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.textBoxQRCode.Location = new System.Drawing.Point(40, 23); - this.textBoxQRCode.Name = "textBoxQRCode"; - this.textBoxQRCode.Size = new System.Drawing.Size(394, 20); - this.textBoxQRCode.TabIndex = 1; - this.textBoxQRCode.Text = "1234567890"; - this.textBoxQRCode.TextChanged += new System.EventHandler(this.textBoxQRCode_TextChanged); - // - // pictureBoxQRCode - // - this.pictureBoxQRCode.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.pictureBoxQRCode.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; - this.pictureBoxQRCode.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.pictureBoxQRCode.Location = new System.Drawing.Point(40, 106); - this.pictureBoxQRCode.Name = "pictureBoxQRCode"; - this.pictureBoxQRCode.Size = new System.Drawing.Size(462, 418); - this.pictureBoxQRCode.TabIndex = 2; - this.pictureBoxQRCode.TabStop = false; - // - // comboBoxECC - // - this.comboBoxECC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.comboBoxECC.FormattingEnabled = true; - this.comboBoxECC.Items.AddRange(new object[] { - "L", - "M", - "Q", - "H"}); - this.comboBoxECC.Location = new System.Drawing.Point(97, 49); - this.comboBoxECC.Name = "comboBoxECC"; - this.comboBoxECC.Size = new System.Drawing.Size(125, 21); - this.comboBoxECC.TabIndex = 3; - this.comboBoxECC.SelectedIndexChanged += new System.EventHandler(this.comboBoxECC_SelectedIndexChanged); - // - // labelECC - // - this.labelECC.AutoSize = true; - this.labelECC.Location = new System.Drawing.Point(37, 52); - this.labelECC.Name = "labelECC"; - this.labelECC.Size = new System.Drawing.Size(57, 13); - this.labelECC.TabIndex = 4; - this.labelECC.Text = "ECC-Level"; - // - // labelIcon - // - this.labelIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.labelIcon.AutoSize = true; - this.labelIcon.Location = new System.Drawing.Point(249, 537); - this.labelIcon.Name = "labelIcon"; - this.labelIcon.Size = new System.Drawing.Size(31, 13); - this.labelIcon.TabIndex = 5; - this.labelIcon.Text = "Icon:"; - // - // iconPath - // - this.iconPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.iconPath.Location = new System.Drawing.Point(286, 534); - this.iconPath.Name = "iconPath"; - this.iconPath.Size = new System.Drawing.Size(148, 20); - this.iconPath.TabIndex = 6; - // - // selectIconBtn - // - this.selectIconBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.selectIconBtn.Location = new System.Drawing.Point(441, 531); - this.selectIconBtn.Name = "selectIconBtn"; - this.selectIconBtn.Size = new System.Drawing.Size(61, 25); - this.selectIconBtn.TabIndex = 7; - this.selectIconBtn.Text = "Select"; - this.selectIconBtn.UseVisualStyleBackColor = true; - this.selectIconBtn.Click += new System.EventHandler(this.selectIconBtn_Click); - // - // labelIconsize - // - this.labelIconsize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.labelIconsize.AutoSize = true; - this.labelIconsize.Location = new System.Drawing.Point(42, 537); - this.labelIconsize.Name = "labelIconsize"; - this.labelIconsize.Size = new System.Drawing.Size(52, 13); - this.labelIconsize.TabIndex = 8; - this.labelIconsize.Text = "Icon size:"; - // - // iconSize - // - this.iconSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.iconSize.Location = new System.Drawing.Point(100, 534); - this.iconSize.Name = "iconSize"; - this.iconSize.Size = new System.Drawing.Size(125, 20); - this.iconSize.TabIndex = 9; - // - // buttonSave - // - this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonSave.Location = new System.Drawing.Point(410, 561); - this.buttonSave.Name = "buttonSave"; - this.buttonSave.Size = new System.Drawing.Size(92, 25); - this.buttonSave.TabIndex = 10; - this.buttonSave.Text = "Save QR code"; - this.buttonSave.UseVisualStyleBackColor = true; - this.buttonSave.Click += new System.EventHandler(this.btn_save_Click); - // - // labelPreviewPrimaryColor - // - this.labelPreviewPrimaryColor.AutoSize = true; - this.labelPreviewPrimaryColor.Location = new System.Drawing.Point(37, 82); - this.labelPreviewPrimaryColor.Name = "labelPreviewPrimaryColor"; - this.labelPreviewPrimaryColor.Size = new System.Drawing.Size(67, 13); - this.labelPreviewPrimaryColor.TabIndex = 12; - this.labelPreviewPrimaryColor.Text = "Primary color"; - // - // panelPreviewPrimaryColor - // - this.panelPreviewPrimaryColor.BackColor = System.Drawing.Color.Black; - this.panelPreviewPrimaryColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.panelPreviewPrimaryColor.Location = new System.Drawing.Point(174, 76); - this.panelPreviewPrimaryColor.Name = "panelPreviewPrimaryColor"; - this.panelPreviewPrimaryColor.Size = new System.Drawing.Size(48, 24); - this.panelPreviewPrimaryColor.TabIndex = 13; - this.panelPreviewPrimaryColor.Click += new System.EventHandler(this.panelPreviewPrimaryColor_Click); - // - // labelPreviewBackgroundColor - // - this.labelPreviewBackgroundColor.AutoSize = true; - this.labelPreviewBackgroundColor.Location = new System.Drawing.Point(249, 82); - this.labelPreviewBackgroundColor.Name = "labelPreviewBackgroundColor"; - this.labelPreviewBackgroundColor.Size = new System.Drawing.Size(91, 13); - this.labelPreviewBackgroundColor.TabIndex = 14; - this.labelPreviewBackgroundColor.Text = "Background color"; - // - // panelPreviewBackgroundColor - // - this.panelPreviewBackgroundColor.BackColor = System.Drawing.Color.White; - this.panelPreviewBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.panelPreviewBackgroundColor.Location = new System.Drawing.Point(386, 76); - this.panelPreviewBackgroundColor.Name = "panelPreviewBackgroundColor"; - this.panelPreviewBackgroundColor.Size = new System.Drawing.Size(48, 24); - this.panelPreviewBackgroundColor.TabIndex = 15; - this.panelPreviewBackgroundColor.Click += new System.EventHandler(this.panelPreviewBackgroundColor_Click); - // - // Form1 - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(543, 598); - this.Controls.Add(this.panelPreviewBackgroundColor); - this.Controls.Add(this.labelPreviewBackgroundColor); - this.Controls.Add(this.panelPreviewPrimaryColor); - this.Controls.Add(this.labelPreviewPrimaryColor); - this.Controls.Add(this.buttonSave); - this.Controls.Add(this.iconSize); - this.Controls.Add(this.labelIconsize); - this.Controls.Add(this.selectIconBtn); - this.Controls.Add(this.iconPath); - this.Controls.Add(this.labelIcon); - this.Controls.Add(this.labelECC); - this.Controls.Add(this.comboBoxECC); - this.Controls.Add(this.pictureBoxQRCode); - this.Controls.Add(this.textBoxQRCode); - this.Controls.Add(this.buttonGenerate); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MinimumSize = new System.Drawing.Size(559, 602); - this.Name = "Form1"; - this.Text = "QRCoder"; - this.Load += new System.EventHandler(this.Form1_Load); - ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQRCode)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.iconSize)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); + this.buttonGenerate = new System.Windows.Forms.Button(); + this.textBoxQRCode = new System.Windows.Forms.TextBox(); + this.pictureBoxQRCode = new System.Windows.Forms.PictureBox(); + this.comboBoxECC = new System.Windows.Forms.ComboBox(); + this.labelECC = new System.Windows.Forms.Label(); + this.labelIcon = new System.Windows.Forms.Label(); + this.iconPath = new System.Windows.Forms.TextBox(); + this.selectIconBtn = new System.Windows.Forms.Button(); + this.labelIconsize = new System.Windows.Forms.Label(); + this.iconSize = new System.Windows.Forms.NumericUpDown(); + this.buttonSave = new System.Windows.Forms.Button(); + this.colorDialogPrimaryColor = new System.Windows.Forms.ColorDialog(); + this.labelPreviewPrimaryColor = new System.Windows.Forms.Label(); + this.panelPreviewPrimaryColor = new System.Windows.Forms.Panel(); + this.labelPreviewBackgroundColor = new System.Windows.Forms.Label(); + this.panelPreviewBackgroundColor = new System.Windows.Forms.Panel(); + this.colorDialogBackgroundColor = new System.Windows.Forms.ColorDialog(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQRCode)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.iconSize)).BeginInit(); + this.SuspendLayout(); + // + // buttonGenerate + // + this.buttonGenerate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.buttonGenerate.Location = new System.Drawing.Point(440, 23); + this.buttonGenerate.Name = "buttonGenerate"; + this.buttonGenerate.Size = new System.Drawing.Size(62, 23); + this.buttonGenerate.TabIndex = 0; + this.buttonGenerate.Text = "Generate"; + this.buttonGenerate.UseVisualStyleBackColor = true; + this.buttonGenerate.Click += new System.EventHandler(this.buttonGenerate_Click); + // + // textBoxQRCode + // + this.textBoxQRCode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBoxQRCode.Location = new System.Drawing.Point(40, 23); + this.textBoxQRCode.Name = "textBoxQRCode"; + this.textBoxQRCode.Size = new System.Drawing.Size(394, 20); + this.textBoxQRCode.TabIndex = 1; + this.textBoxQRCode.Text = "1234567890"; + this.textBoxQRCode.TextChanged += new System.EventHandler(this.textBoxQRCode_TextChanged); + // + // pictureBoxQRCode + // + this.pictureBoxQRCode.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBoxQRCode.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.pictureBoxQRCode.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pictureBoxQRCode.Location = new System.Drawing.Point(40, 106); + this.pictureBoxQRCode.Name = "pictureBoxQRCode"; + this.pictureBoxQRCode.Size = new System.Drawing.Size(462, 418); + this.pictureBoxQRCode.TabIndex = 2; + this.pictureBoxQRCode.TabStop = false; + // + // comboBoxECC + // + this.comboBoxECC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBoxECC.FormattingEnabled = true; + this.comboBoxECC.Items.AddRange(new object[] { + "L", + "M", + "Q", + "H"}); + this.comboBoxECC.Location = new System.Drawing.Point(97, 49); + this.comboBoxECC.Name = "comboBoxECC"; + this.comboBoxECC.Size = new System.Drawing.Size(125, 21); + this.comboBoxECC.TabIndex = 3; + this.comboBoxECC.SelectedIndexChanged += new System.EventHandler(this.comboBoxECC_SelectedIndexChanged); + // + // labelECC + // + this.labelECC.AutoSize = true; + this.labelECC.Location = new System.Drawing.Point(37, 52); + this.labelECC.Name = "labelECC"; + this.labelECC.Size = new System.Drawing.Size(57, 13); + this.labelECC.TabIndex = 4; + this.labelECC.Text = "ECC-Level"; + // + // labelIcon + // + this.labelIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelIcon.AutoSize = true; + this.labelIcon.Location = new System.Drawing.Point(249, 537); + this.labelIcon.Name = "labelIcon"; + this.labelIcon.Size = new System.Drawing.Size(31, 13); + this.labelIcon.TabIndex = 5; + this.labelIcon.Text = "Icon:"; + // + // iconPath + // + this.iconPath.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.iconPath.Location = new System.Drawing.Point(286, 534); + this.iconPath.Name = "iconPath"; + this.iconPath.Size = new System.Drawing.Size(148, 20); + this.iconPath.TabIndex = 6; + // + // selectIconBtn + // + this.selectIconBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.selectIconBtn.Location = new System.Drawing.Point(441, 531); + this.selectIconBtn.Name = "selectIconBtn"; + this.selectIconBtn.Size = new System.Drawing.Size(61, 25); + this.selectIconBtn.TabIndex = 7; + this.selectIconBtn.Text = "Select"; + this.selectIconBtn.UseVisualStyleBackColor = true; + this.selectIconBtn.Click += new System.EventHandler(this.selectIconBtn_Click); + // + // labelIconsize + // + this.labelIconsize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.labelIconsize.AutoSize = true; + this.labelIconsize.Location = new System.Drawing.Point(42, 537); + this.labelIconsize.Name = "labelIconsize"; + this.labelIconsize.Size = new System.Drawing.Size(52, 13); + this.labelIconsize.TabIndex = 8; + this.labelIconsize.Text = "Icon size:"; + // + // iconSize + // + this.iconSize.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.iconSize.Location = new System.Drawing.Point(100, 534); + this.iconSize.Name = "iconSize"; + this.iconSize.Size = new System.Drawing.Size(125, 20); + this.iconSize.TabIndex = 9; + // + // buttonSave + // + this.buttonSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonSave.Location = new System.Drawing.Point(410, 561); + this.buttonSave.Name = "buttonSave"; + this.buttonSave.Size = new System.Drawing.Size(92, 25); + this.buttonSave.TabIndex = 10; + this.buttonSave.Text = "Save QR code"; + this.buttonSave.UseVisualStyleBackColor = true; + this.buttonSave.Click += new System.EventHandler(this.btn_save_Click); + // + // labelPreviewPrimaryColor + // + this.labelPreviewPrimaryColor.AutoSize = true; + this.labelPreviewPrimaryColor.Location = new System.Drawing.Point(37, 82); + this.labelPreviewPrimaryColor.Name = "labelPreviewPrimaryColor"; + this.labelPreviewPrimaryColor.Size = new System.Drawing.Size(67, 13); + this.labelPreviewPrimaryColor.TabIndex = 12; + this.labelPreviewPrimaryColor.Text = "Primary color"; + // + // panelPreviewPrimaryColor + // + this.panelPreviewPrimaryColor.BackColor = System.Drawing.Color.Black; + this.panelPreviewPrimaryColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelPreviewPrimaryColor.Location = new System.Drawing.Point(174, 76); + this.panelPreviewPrimaryColor.Name = "panelPreviewPrimaryColor"; + this.panelPreviewPrimaryColor.Size = new System.Drawing.Size(48, 24); + this.panelPreviewPrimaryColor.TabIndex = 13; + this.panelPreviewPrimaryColor.Click += new System.EventHandler(this.panelPreviewPrimaryColor_Click); + // + // labelPreviewBackgroundColor + // + this.labelPreviewBackgroundColor.AutoSize = true; + this.labelPreviewBackgroundColor.Location = new System.Drawing.Point(249, 82); + this.labelPreviewBackgroundColor.Name = "labelPreviewBackgroundColor"; + this.labelPreviewBackgroundColor.Size = new System.Drawing.Size(91, 13); + this.labelPreviewBackgroundColor.TabIndex = 14; + this.labelPreviewBackgroundColor.Text = "Background color"; + // + // panelPreviewBackgroundColor + // + this.panelPreviewBackgroundColor.BackColor = System.Drawing.Color.White; + this.panelPreviewBackgroundColor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelPreviewBackgroundColor.Location = new System.Drawing.Point(386, 76); + this.panelPreviewBackgroundColor.Name = "panelPreviewBackgroundColor"; + this.panelPreviewBackgroundColor.Size = new System.Drawing.Size(48, 24); + this.panelPreviewBackgroundColor.TabIndex = 15; + this.panelPreviewBackgroundColor.Click += new System.EventHandler(this.panelPreviewBackgroundColor_Click); + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(543, 598); + this.Controls.Add(this.panelPreviewBackgroundColor); + this.Controls.Add(this.labelPreviewBackgroundColor); + this.Controls.Add(this.panelPreviewPrimaryColor); + this.Controls.Add(this.labelPreviewPrimaryColor); + this.Controls.Add(this.buttonSave); + this.Controls.Add(this.iconSize); + this.Controls.Add(this.labelIconsize); + this.Controls.Add(this.selectIconBtn); + this.Controls.Add(this.iconPath); + this.Controls.Add(this.labelIcon); + this.Controls.Add(this.labelECC); + this.Controls.Add(this.comboBoxECC); + this.Controls.Add(this.pictureBoxQRCode); + this.Controls.Add(this.textBoxQRCode); + this.Controls.Add(this.buttonGenerate); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(559, 602); + this.Name = "Form1"; + this.Text = "QRCoder"; + this.Load += new System.EventHandler(this.Form1_Load); + ((System.ComponentModel.ISupportInitialize)(this.pictureBoxQRCode)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.iconSize)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); - } + } - #endregion + #endregion - private System.Windows.Forms.Button buttonGenerate; - private System.Windows.Forms.TextBox textBoxQRCode; - private System.Windows.Forms.PictureBox pictureBoxQRCode; - private System.Windows.Forms.ComboBox comboBoxECC; - private System.Windows.Forms.Label labelECC; - private System.Windows.Forms.Label labelIcon; - private System.Windows.Forms.TextBox iconPath; - private System.Windows.Forms.Button selectIconBtn; - private System.Windows.Forms.Label labelIconsize; - private System.Windows.Forms.NumericUpDown iconSize; - private System.Windows.Forms.Button buttonSave; - private System.Windows.Forms.ColorDialog colorDialogPrimaryColor; - private System.Windows.Forms.Label labelPreviewPrimaryColor; - private System.Windows.Forms.Panel panelPreviewPrimaryColor; - private System.Windows.Forms.Label labelPreviewBackgroundColor; - private System.Windows.Forms.Panel panelPreviewBackgroundColor; - private System.Windows.Forms.ColorDialog colorDialogBackgroundColor; - } + private System.Windows.Forms.Button buttonGenerate; + private System.Windows.Forms.TextBox textBoxQRCode; + private System.Windows.Forms.PictureBox pictureBoxQRCode; + private System.Windows.Forms.ComboBox comboBoxECC; + private System.Windows.Forms.Label labelECC; + private System.Windows.Forms.Label labelIcon; + private System.Windows.Forms.TextBox iconPath; + private System.Windows.Forms.Button selectIconBtn; + private System.Windows.Forms.Label labelIconsize; + private System.Windows.Forms.NumericUpDown iconSize; + private System.Windows.Forms.Button buttonSave; + private System.Windows.Forms.ColorDialog colorDialogPrimaryColor; + private System.Windows.Forms.Label labelPreviewPrimaryColor; + private System.Windows.Forms.Panel panelPreviewPrimaryColor; + private System.Windows.Forms.Label labelPreviewBackgroundColor; + private System.Windows.Forms.Panel panelPreviewBackgroundColor; + private System.Windows.Forms.ColorDialog colorDialogBackgroundColor; } diff --git a/QRCoderDemo/Form1.cs b/QRCoderDemo/Form1.cs index a955d644..57edab5f 100644 --- a/QRCoderDemo/Form1.cs +++ b/QRCoderDemo/Form1.cs @@ -1,175 +1,141 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; using System.Drawing; -using System.Linq; -using System.Text; +using System.Drawing.Imaging; using System.Windows.Forms; using QRCoder; -using System.Drawing.Imaging; -using System.IO; -namespace QRCoderDemo +namespace QRCoderDemo; + +public partial class Form1 : Form { - public partial class Form1 : Form + public Form1() { - public Form1() - { - InitializeComponent(); - } + InitializeComponent(); + } + + private void Form1_Load(object sender, EventArgs e) + { + comboBoxECC.SelectedIndex = 0; //Pre-select ECC level "L" + RenderQrCode(); + } + + private void buttonGenerate_Click(object sender, EventArgs e) + => RenderQrCode(); + + private void RenderQrCode() + { + string level = comboBoxECC.SelectedItem.ToString(); + var eccLevel = (QRCodeGenerator.ECCLevel)(level == "L" ? 0 : level == "M" ? 1 : level == "Q" ? 2 : 3); + using var qrGenerator = new QRCodeGenerator(); + using var qrCodeData = qrGenerator.CreateQrCode(textBoxQRCode.Text, eccLevel); + using var qrCode = new QRCode(qrCodeData); + pictureBoxQRCode.BackgroundImage = qrCode.GetGraphic(20, GetPrimaryColor(), GetBackgroundColor(), + GetIconBitmap(), (int)iconSize.Value); + + pictureBoxQRCode.Size = new System.Drawing.Size(pictureBoxQRCode.Width, pictureBoxQRCode.Height); + //Set the SizeMode to center the image. + pictureBoxQRCode.SizeMode = PictureBoxSizeMode.CenterImage; + + pictureBoxQRCode.SizeMode = PictureBoxSizeMode.StretchImage; + } - private void Form1_Load(object sender, EventArgs e) + private Bitmap GetIconBitmap() + { + if (iconPath.Text.Length == 0) { - comboBoxECC.SelectedIndex = 0; //Pre-select ECC level "L" - RenderQrCode(); + return null; } - - private void buttonGenerate_Click(object sender, EventArgs e) + try { - RenderQrCode(); + return new Bitmap(iconPath.Text); } - - private void RenderQrCode() + catch (Exception) { - string level = comboBoxECC.SelectedItem.ToString(); - QRCodeGenerator.ECCLevel eccLevel = (QRCodeGenerator.ECCLevel)(level == "L" ? 0 : level == "M" ? 1 : level == "Q" ? 2 : 3); - using (QRCodeGenerator qrGenerator = new QRCodeGenerator()) - using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(textBoxQRCode.Text, eccLevel)) - using (QRCode qrCode = new QRCode(qrCodeData)) - { - pictureBoxQRCode.BackgroundImage = qrCode.GetGraphic(20, GetPrimaryColor(), GetBackgroundColor(), - GetIconBitmap(), (int)iconSize.Value); - - this.pictureBoxQRCode.Size = new System.Drawing.Size(pictureBoxQRCode.Width, pictureBoxQRCode.Height); - //Set the SizeMode to center the image. - this.pictureBoxQRCode.SizeMode = PictureBoxSizeMode.CenterImage; - - pictureBoxQRCode.SizeMode = PictureBoxSizeMode.StretchImage; - } + return null; } + } - private Bitmap GetIconBitmap() + private void selectIconBtn_Click(object sender, EventArgs e) + { + var openFileDlg = new OpenFileDialog { - if (iconPath.Text.Length == 0) - { - return null; - } - try - { - return new Bitmap(iconPath.Text); - } - catch (Exception) + Title = "Select icon", + Multiselect = false, + CheckFileExists = true + }; + if (openFileDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + iconPath.Text = openFileDlg.FileName; + if (iconSize.Value == 0) { - return null; + iconSize.Value = 15; } } - - private void selectIconBtn_Click(object sender, EventArgs e) + else { - OpenFileDialog openFileDlg = new OpenFileDialog(); - openFileDlg.Title = "Select icon"; - openFileDlg.Multiselect = false; - openFileDlg.CheckFileExists = true; - if (openFileDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) - { - iconPath.Text = openFileDlg.FileName; - if (iconSize.Value == 0) - { - iconSize.Value = 15; - } - } - else - { - iconPath.Text = ""; - } + iconPath.Text = ""; } + } - private void btn_save_Click(object sender, EventArgs e) - { + private void btn_save_Click(object sender, EventArgs e) + { - // Displays a SaveFileDialog so the user can save the Image - SaveFileDialog saveFileDialog1 = new SaveFileDialog(); - saveFileDialog1.Filter = "Bitmap Image|*.bmp|PNG Image|*.png|JPeg Image|*.jpg|Gif Image|*.gif"; - saveFileDialog1.Title = "Save an Image File"; - saveFileDialog1.ShowDialog(); + // Displays a SaveFileDialog so the user can save the Image + var saveFileDialog1 = new SaveFileDialog + { + Filter = "Bitmap Image|*.bmp|PNG Image|*.png|JPeg Image|*.jpg|Gif Image|*.gif", + Title = "Save an Image File" + }; + saveFileDialog1.ShowDialog(); - // If the file name is not an empty string open it for saving. - if (saveFileDialog1.FileName != "") + // If the file name is not an empty string open it for saving. + if (saveFileDialog1.FileName != "") + { + // Saves the Image via a FileStream created by the OpenFile method. + using var fs = (System.IO.FileStream)saveFileDialog1.OpenFile(); + // Saves the Image in the appropriate ImageFormat based upon the + // File type selected in the dialog box. + // NOTE that the FilterIndex property is one-based. + + ImageFormat imageFormat = null; + imageFormat = saveFileDialog1.FilterIndex switch { - // Saves the Image via a FileStream created by the OpenFile method. - using (FileStream fs = (System.IO.FileStream) saveFileDialog1.OpenFile()) - { - // Saves the Image in the appropriate ImageFormat based upon the - // File type selected in the dialog box. - // NOTE that the FilterIndex property is one-based. - - ImageFormat imageFormat = null; - switch (saveFileDialog1.FilterIndex) - { - case 1: - imageFormat = ImageFormat.Bmp; - break; - case 2: - imageFormat = ImageFormat.Png; - break; - case 3: - imageFormat = ImageFormat.Jpeg; - break; - case 4: - imageFormat = ImageFormat.Gif; - break; - default: - throw new NotSupportedException("File extension is not supported"); - } - - pictureBoxQRCode.BackgroundImage.Save(fs, imageFormat); - } - } + 1 => ImageFormat.Bmp, + 2 => ImageFormat.Png, + 3 => ImageFormat.Jpeg, + 4 => ImageFormat.Gif, + _ => throw new NotSupportedException("File extension is not supported"), + }; + pictureBoxQRCode.BackgroundImage.Save(fs, imageFormat); } + } - public void ExportToBmp(string path) - { + private void textBoxQRCode_TextChanged(object sender, EventArgs e) + => RenderQrCode(); - } + private void comboBoxECC_SelectedIndexChanged(object sender, EventArgs e) + => RenderQrCode(); - private void textBoxQRCode_TextChanged(object sender, EventArgs e) + private void panelPreviewPrimaryColor_Click(object sender, EventArgs e) + { + if (colorDialogPrimaryColor.ShowDialog() == DialogResult.OK) { + panelPreviewPrimaryColor.BackColor = colorDialogPrimaryColor.Color; RenderQrCode(); } + } - private void comboBoxECC_SelectedIndexChanged(object sender, EventArgs e) + private void panelPreviewBackgroundColor_Click(object sender, EventArgs e) + { + if (colorDialogBackgroundColor.ShowDialog() == DialogResult.OK) { + panelPreviewBackgroundColor.BackColor = colorDialogBackgroundColor.Color; RenderQrCode(); } + } - private void panelPreviewPrimaryColor_Click(object sender, EventArgs e) - { - if (colorDialogPrimaryColor.ShowDialog() == DialogResult.OK) - { - panelPreviewPrimaryColor.BackColor = colorDialogPrimaryColor.Color; - RenderQrCode(); - } - } - - private void panelPreviewBackgroundColor_Click(object sender, EventArgs e) - { - if (colorDialogBackgroundColor.ShowDialog() == DialogResult.OK) - { - panelPreviewBackgroundColor.BackColor = colorDialogBackgroundColor.Color; - RenderQrCode(); - } - } - - private Color GetPrimaryColor() - { - return panelPreviewPrimaryColor.BackColor; - } + private Color GetPrimaryColor() => panelPreviewPrimaryColor.BackColor; - private Color GetBackgroundColor() - { - return panelPreviewBackgroundColor.BackColor; - } - } + private Color GetBackgroundColor() => panelPreviewBackgroundColor.BackColor; } diff --git a/QRCoderDemo/Program.cs b/QRCoderDemo/Program.cs index 493813c2..6edeabb8 100644 --- a/QRCoderDemo/Program.cs +++ b/QRCoderDemo/Program.cs @@ -1,22 +1,21 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; -namespace QRCoderDemo +namespace QRCoderDemo; + +internal static class Program { - static class Program + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + private static void Main() { - /// - /// Der Haupteinstiegspunkt für die Anwendung. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - //Application.Run(new Form1()); - Application.Run(new Form1()); - } + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + //Application.Run(new Form1()); + Application.Run(new Form1()); } } diff --git a/QRCoderDemo/Properties/AssemblyInfo.cs b/QRCoderDemo/Properties/AssemblyInfo.cs index 6751eac2..28432016 100644 --- a/QRCoderDemo/Properties/AssemblyInfo.cs +++ b/QRCoderDemo/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +using System.Reflection; using System.Runtime.InteropServices; // Allgemeine Informationen über eine Assembly werden über die folgenden diff --git a/QRCoderDemo/QRCoderDemo.csproj b/QRCoderDemo/QRCoderDemo.csproj index 01aaef86..496e9820 100644 --- a/QRCoderDemo/QRCoderDemo.csproj +++ b/QRCoderDemo/QRCoderDemo.csproj @@ -1,4 +1,4 @@ - + net45;net5.0-windows diff --git a/QRCoderDemoUWP/App.xaml.cs b/QRCoderDemoUWP/App.xaml.cs index 2c90273f..91a29799 100644 --- a/QRCoderDemoUWP/App.xaml.cs +++ b/QRCoderDemoUWP/App.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,92 +15,88 @@ using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Navigation; -namespace QRCoderDemoUWP +namespace QRCoderDemoUWP; + +/// +/// Provides application-specific behavior to supplement the default Application class. +/// +public sealed partial class App : Application { /// - /// Provides application-specific behavior to supplement the default Application class. + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). /// - sealed partial class App : Application + public App() { - /// - /// Initializes the singleton application object. This is the first line of authored code - /// executed, and as such is the logical equivalent of main() or WinMain(). - /// - public App() - { - this.InitializeComponent(); - this.Suspending += OnSuspending; - } + InitializeComponent(); + Suspending += OnSuspending; + } - /// - /// Invoked when the application is launched normally by the end user. Other entry points - /// will be used such as when the application is launched to open a specific file. - /// - /// Details about the launch request and process. - protected override void OnLaunched(LaunchActivatedEventArgs e) - { + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + protected override void OnLaunched(LaunchActivatedEventArgs e) + { #if DEBUG - if (System.Diagnostics.Debugger.IsAttached) - { - this.DebugSettings.EnableFrameRateCounter = true; - } + if (System.Diagnostics.Debugger.IsAttached) + { + DebugSettings.EnableFrameRateCounter = true; + } #endif - Frame rootFrame = Window.Current.Content as Frame; - - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (rootFrame == null) - { - // Create a Frame to act as the navigation context and navigate to the first page - rootFrame = new Frame(); - - rootFrame.NavigationFailed += OnNavigationFailed; - if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) - { - //TODO: Load state from previously suspended application - } + // Do not repeat app initialization when the Window already has content, + // just ensure that the window is active + if (!(Window.Current.Content is Frame rootFrame)) + { + // Create a Frame to act as the navigation context and navigate to the first page + rootFrame = new Frame(); - // Place the frame in the current Window - Window.Current.Content = rootFrame; - } + rootFrame.NavigationFailed += OnNavigationFailed; - if (e.PrelaunchActivated == false) + if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { - if (rootFrame.Content == null) - { - // When the navigation stack isn't restored navigate to the first page, - // configuring the new page by passing required information as a navigation - // parameter - rootFrame.Navigate(typeof(MainPage), e.Arguments); - } - // Ensure the current window is active - Window.Current.Activate(); + //TODO: Load state from previously suspended application } - } - /// - /// Invoked when Navigation to a certain page fails - /// - /// The Frame which failed navigation - /// Details about the navigation failure - void OnNavigationFailed(object sender, NavigationFailedEventArgs e) - { - throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + // Place the frame in the current Window + Window.Current.Content = rootFrame; } - /// - /// Invoked when application execution is being suspended. Application state is saved - /// without knowing whether the application will be terminated or resumed with the contents - /// of memory still intact. - /// - /// The source of the suspend request. - /// Details about the suspend request. - private void OnSuspending(object sender, SuspendingEventArgs e) + if (e.PrelaunchActivated == false) { - var deferral = e.SuspendingOperation.GetDeferral(); - //TODO: Save application state and stop any background activity - deferral.Complete(); + if (rootFrame.Content == null) + { + // When the navigation stack isn't restored navigate to the first page, + // configuring the new page by passing required information as a navigation + // parameter + rootFrame.Navigate(typeof(MainPage), e.Arguments); + } + // Ensure the current window is active + Window.Current.Activate(); } } + + /// + /// Invoked when Navigation to a certain page fails + /// + /// The Frame which failed navigation + /// Details about the navigation failure + private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) + => throw new Exception("Failed to load Page " + e.SourcePageType.FullName); + + /// + /// Invoked when application execution is being suspended. Application state is saved + /// without knowing whether the application will be terminated or resumed with the contents + /// of memory still intact. + /// + /// The source of the suspend request. + /// Details about the suspend request. + private void OnSuspending(object sender, SuspendingEventArgs e) + { + var deferral = e.SuspendingOperation.GetDeferral(); + //TODO: Save application state and stop any background activity + deferral.Complete(); + } } diff --git a/QRCoderDemoUWP/MainPage.xaml.cs b/QRCoderDemoUWP/MainPage.xaml.cs index 082ccee1..7b2fca02 100644 --- a/QRCoderDemoUWP/MainPage.xaml.cs +++ b/QRCoderDemoUWP/MainPage.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Windows.Storage.Streams; @@ -9,73 +9,68 @@ // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 -namespace QRCoderDemoUWP +namespace QRCoderDemoUWP; + +/// +/// An empty page that can be used on its own or navigated to within a Frame. +/// +public sealed partial class MainPage : Page { - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class MainPage : Page + public MainPage() { - public MainPage() - { - this.InitializeComponent(); - this.DataContext = this; - comboBoxECC.SelectedIndex = 0; - } + InitializeComponent(); + DataContext = this; + comboBoxECC.SelectedIndex = 0; + } - private async void button_Click(object sender, RoutedEventArgs e) +#pragma warning disable IDE1006 // Naming Styles + private async void button_Click(object sender, RoutedEventArgs e) +#pragma warning restore IDE1006 // Naming Styles + { + if (comboBoxECC.SelectedItem != null) { - if (comboBoxECC.SelectedItem != null) - { - //Create generator - string level = comboBoxECC.SelectedItem.ToString(); - QRCodeGenerator.ECCLevel eccLevel = (QRCodeGenerator.ECCLevel)(level == "L" ? 0 : level == "M" ? 1 : level == "Q" ? 2 : 3); + //Create generator + string level = comboBoxECC.SelectedItem.ToString(); + var eccLevel = (QRCodeGenerator.ECCLevel)(level == "L" ? 0 : level == "M" ? 1 : level == "Q" ? 2 : 3); - //Create raw qr code data - QRCodeGenerator qrGenerator = new QRCodeGenerator(); - QRCodeData qrCodeData = qrGenerator.CreateQrCode("The text which should be encoded.", eccLevel); + //Create raw qr code data + var qrGenerator = new QRCodeGenerator(); + var qrCodeData = qrGenerator.CreateQrCode("The text which should be encoded.", eccLevel); - //Create byte/raw bitmap qr code - BitmapByteQRCode qrCodeBmp = new BitmapByteQRCode(qrCodeData); - byte[] qrCodeImageBmp = qrCodeBmp.GetGraphic(20, new byte[] { 118, 126, 152 },new byte[] { 144, 201, 111 }); - using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) + //Create byte/raw bitmap qr code + var qrCodeBmp = new BitmapByteQRCode(qrCodeData); + byte[] qrCodeImageBmp = qrCodeBmp.GetGraphic(20, new byte[] { 118, 126, 152 }, new byte[] { 144, 201, 111 }); + using (var stream = new InMemoryRandomAccessStream()) + { + using (var writer = new DataWriter(stream.GetOutputStreamAt(0))) { - using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0))) - { - writer.WriteBytes(qrCodeImageBmp); - await writer.StoreAsync(); - } - var image = new BitmapImage(); - await image.SetSourceAsync(stream); - - imageViewerBmp.Source = image; + writer.WriteBytes(qrCodeImageBmp); + await writer.StoreAsync(); } + var image = new BitmapImage(); + await image.SetSourceAsync(stream); - //Create byte/raw png qr code - PngByteQRCode qrCodePng = new PngByteQRCode(qrCodeData); - byte[] qrCodeImagePng = qrCodePng.GetGraphic(20, new byte[] { 144, 201, 111 }, new byte[] { 118, 126, 152 }); - using (InMemoryRandomAccessStream stream = new InMemoryRandomAccessStream()) - { - using (DataWriter writer = new DataWriter(stream.GetOutputStreamAt(0))) - { - writer.WriteBytes(qrCodeImagePng); - await writer.StoreAsync(); - } - var image = new BitmapImage(); - await image.SetSourceAsync(stream); - - imageViewerPng.Source = image; - } + imageViewerBmp.Source = image; } - } - public List EccModes - { - get + //Create byte/raw png qr code + var qrCodePng = new PngByteQRCode(qrCodeData); + byte[] qrCodeImagePng = qrCodePng.GetGraphic(20, new byte[] { 144, 201, 111 }, new byte[] { 118, 126, 152 }); + using (var stream = new InMemoryRandomAccessStream()) { - return Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast().Select(x => x.ToString()).ToList(); + using (var writer = new DataWriter(stream.GetOutputStreamAt(0))) + { + writer.WriteBytes(qrCodeImagePng); + await writer.StoreAsync(); + } + var image = new BitmapImage(); + await image.SetSourceAsync(stream); + + imageViewerPng.Source = image; } } } + + public List EccModes => Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast().Select(x => x.ToString()).ToList(); } diff --git a/QRCoderTests/ArtQRCodeRendererTests.cs b/QRCoderTests/ArtQRCodeRendererTests.cs index 111d6979..cec11dac 100644 --- a/QRCoderTests/ArtQRCodeRendererTests.cs +++ b/QRCoderTests/ArtQRCodeRendererTests.cs @@ -1,106 +1,105 @@ -#if SYSTEM_DRAWING +#if SYSTEM_DRAWING -using Xunit; -using QRCoder; -using Shouldly; using System.Drawing; -using QRCoderTests.Helpers.XUnitExtenstions; +using QRCoder; using QRCoderTests.Helpers; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; + +namespace QRCoderTests; -namespace QRCoderTests + +public class ArtQRCodeRendererTests { - public class ArtQRCodeRendererTests + + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_create_standard_qrcode_graphic() { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new ArtQRCode(data).GetGraphic(10); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("df510ce9feddc0dd8c23c54e700abbf0"); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_create_standard_qrcode_graphic() + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_create_standard_qrcode_graphic_with_custom_finder() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var finder = new Bitmap(70, 70); + using (var g = Graphics.FromImage(finder)) { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new ArtQRCode(data).GetGraphic(10); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("df510ce9feddc0dd8c23c54e700abbf0"); + g.FillRectangle(Brushes.Red, 0, 0, 70, 70); } + var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, finderPatternImage: finder); - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_create_standard_qrcode_graphic_with_custom_finder() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var finder = new Bitmap(70, 70); - using (var g = Graphics.FromImage(finder)) - { - g.FillRectangle(Brushes.Red, 0, 0, 70, 70); - } - var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, finderPatternImage: finder); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("e28a3779b9b975b85984e36f596c9a35"); - } + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("e28a3779b9b975b85984e36f596c9a35"); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_create_standard_qrcode_graphic_without_quietzone() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, drawQuietZones: false); + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_create_standard_qrcode_graphic_without_quietzone() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new ArtQRCode(data).GetGraphic(10, Color.Black, Color.White, Color.Transparent, drawQuietZones: false); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("54408da26852d6c67ab7cad2656da7fa"); - } + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("54408da26852d6c67ab7cad2656da7fa"); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_create_standard_qrcode_graphic_with_background() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new ArtQRCode(data).GetGraphic((Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_create_standard_qrcode_graphic_with_background() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new ArtQRCode(data).GetGraphic((Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); + var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("7f039ccde219ae78e4f768466376a17f"); - } + result.ShouldBe("7f039ccde219ae78e4f768466376a17f"); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void should_throw_pixelfactor_oor_exception() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var aCode = new ArtQRCode(data); - - var exception = Record.Exception(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The parameter pixelSize must be between 0 and 1. (0-100%)"); - } + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void should_throw_pixelfactor_oor_exception() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var aCode = new ArtQRCode(data); + + var exception = Record.Exception(() => aCode.GetGraphic(10, Color.Black, Color.White, Color.Transparent, pixelSizeFactor: 2)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The parameter pixelSize must be between 0 and 1. (0-100%)"); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_instantate_parameterless() - { - var artCode = new ArtQRCode(); - artCode.ShouldNotBeNull(); - artCode.ShouldBeOfType(); - } + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_instantate_parameterless() + { + var artCode = new ArtQRCode(); + artCode.ShouldNotBeNull(); + artCode.ShouldBeOfType(); + } - [Fact] - [Category("QRRenderer/ArtQRCode")] - public void can_render_artqrcode_from_helper() - { - //Create QR code - var bmp = ArtQRCodeHelper.GetQRCode("A", 10, Color.Black, Color.White, Color.Transparent, QRCodeGenerator.ECCLevel.L); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("a1975852df9b537344468bd44d54abe0"); - } + [Fact] + [Category("QRRenderer/ArtQRCode")] + public void can_render_artqrcode_from_helper() + { + //Create QR code + var bmp = ArtQRCodeHelper.GetQRCode("A", 10, Color.Black, Color.White, Color.Transparent, QRCodeGenerator.ECCLevel.L); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("a1975852df9b537344468bd44d54abe0"); } } -#endif \ No newline at end of file +#endif diff --git a/QRCoderTests/AsciiQRCodeRendererTests.cs b/QRCoderTests/AsciiQRCodeRendererTests.cs index 1a55e562..09d65789 100644 --- a/QRCoderTests/AsciiQRCodeRendererTests.cs +++ b/QRCoderTests/AsciiQRCodeRendererTests.cs @@ -1,132 +1,131 @@ -using Xunit; using QRCoder; -using Shouldly; using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; + +namespace QRCoderTests; -namespace QRCoderTests + +public class AsciiQRCodeRendererTests { - public class AsciiQRCodeRendererTests - { - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_ascii_qrcode() - { - var targetCode = " \n \n \n \n ██████████████ ████ ██ ██████████████ \n ██ ██ ████ ██ ██ ██ \n ██ ██████ ██ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██████████ ██ ██████ ██ \n ██ ██ ██ ██ \n ██████████████ ██ ██ ██ ██████████████ \n ██████████ \n ████ ██ ████ ██████ ██ ██████████ \n ██ ██ ██ ██ ██ ████ \n ████ ██████ ██████ ██████ ██ \n ████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n ██████████████ ██ ████ ██████ ██ \n ██ ██ ██ ████ ██████ \n ██ ██████ ██ ██████ ████████ ██ ██ \n ██ ██████ ██ ██ ██ ████ \n ██ ██████ ██ ██████ ██ ██ ██ \n ██ ██ ██ ██ ██ ██████ \n ██████████████ ██ ██ ██ ██ ██ ██ \n \n \n \n "; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphic(1); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_small_ascii_qrcode() - { - var targetCode = "█████████████████████████████\n█████████████████████████████\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\n████ █ █ █▄█ █▄█ █ █ ████\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\n█████████████████████████████\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphicSmall(); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_small_ascii_qrcode_without_quietzones() - { - var targetCode = " ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ \n █ █ █▄█ █▄█ █ █ \n █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ \n▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄\n ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀\n▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█\n█▄▄▄▄█▄▄▄████▀▀ █▄█▄\n ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄\n █ █ █ ▀ █▄▀█ ██▄█▄\n █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄\n▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄"; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphicSmall(drawQuietZones: false); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_small_ascii_qrcode_inverted() - { - var targetCode = " \n \n █▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█ \n █ ███ █ ▀ █ ▀ █ ███ █ \n █ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █ \n ▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀ \n ██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄ \n ▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ \n ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀ \n █▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀ \n █ ███ █ █▄█ ▀▄ █ ▀ ▀ \n █ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀ \n ▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀ \n \n "; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphicSmall(invert: true); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_small_ascii_qrcode_with_custom_eol() - { - var targetCode = "█████████████████████████████\r\n█████████████████████████████\r\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\r\n████ █ █ █▄█ █▄█ █ █ ████\r\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\r\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\r\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\r\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\r\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\r\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\r\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\r\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\r\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\r\n█████████████████████████████\r\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphicSmall(endOfLine: "\r\n"); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_ascii_qrcode_without_quietzones() - { - var targetCode = "██████████████ ████ ██ ██████████████\n██ ██ ████ ██ ██ ██\n██ ██████ ██ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██████████ ██ ██████ ██\n██ ██ ██ ██\n██████████████ ██ ██ ██ ██████████████\n ██████████ \n ████ ██ ████ ██████ ██ ██████████\n██ ██ ██ ██ ██ ████\n ████ ██████ ██████ ██████ ██\n████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n██████████████ ██ ████ ██████ ██ \n██ ██ ██ ████ ██████ \n██ ██████ ██ ██████ ████████ ██ ██\n██ ██████ ██ ██ ██ ████ \n██ ██████ ██ ██████ ██ ██ ██\n██ ██ ██ ██ ██ ██████\n██████████████ ██ ██ ██ ██ ██ ██"; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphic(1, drawQuietZones : false); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_ascii_qrcode_with_custom_symbols() - { - var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; - - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("A", QRCodeGenerator.ECCLevel.Q); - var asciiCode = new AsciiQRCode(data).GetGraphic(2, "X", " "); - - asciiCode.ShouldBe(targetCode); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_instantate_parameterless() - { - var asciiCode = new AsciiQRCode(); - asciiCode.ShouldNotBeNull(); - asciiCode.ShouldBeOfType(); - } - - [Fact] - [Category("QRRenderer/AsciiQRCode")] - public void can_render_ascii_qrcode_from_helper() - { - var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; - - //Create QR code - var asciiCode = AsciiQRCodeHelper.GetQRCode("A", 2, "X", " ", QRCodeGenerator.ECCLevel.Q); - asciiCode.ShouldBe(targetCode); - } + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_ascii_qrcode() + { + var targetCode = " \n \n \n \n ██████████████ ████ ██ ██████████████ \n ██ ██ ████ ██ ██ ██ \n ██ ██████ ██ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██ ██ ██ ██████ ██ \n ██ ██████ ██ ██████████ ██ ██████ ██ \n ██ ██ ██ ██ \n ██████████████ ██ ██ ██ ██████████████ \n ██████████ \n ████ ██ ████ ██████ ██ ██████████ \n ██ ██ ██ ██ ██ ████ \n ████ ██████ ██████ ██████ ██ \n ████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n ██████████████ ██ ████ ██████ ██ \n ██ ██ ██ ████ ██████ \n ██ ██████ ██ ██████ ████████ ██ ██ \n ██ ██████ ██ ██ ██ ████ \n ██ ██████ ██ ██████ ██ ██ ██ \n ██ ██ ██ ██ ██ ██████ \n ██████████████ ██ ██ ██ ██ ██ ██ \n \n \n \n "; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphic(1); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_qrcode() + { + var targetCode = "█████████████████████████████\n█████████████████████████████\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\n████ █ █ █▄█ █▄█ █ █ ████\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\n█████████████████████████████\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_qrcode_without_quietzones() + { + var targetCode = " ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ \n █ █ █▄█ █▄█ █ █ \n █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ \n▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄\n ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀\n▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█\n█▄▄▄▄█▄▄▄████▀▀ █▄█▄\n ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄\n █ █ █ ▀ █▄▀█ ██▄█▄\n █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄\n▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(drawQuietZones: false); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_qrcode_inverted() + { + var targetCode = " \n \n █▀▀▀▀▀█ ▄▀ █▄ █▀▀▀▀▀█ \n █ ███ █ ▀ █ ▀ █ ███ █ \n █ ▀▀▀ █ ▀▄▄▄▄ █ ▀▀▀ █ \n ▀▀▀▀▀▀▀ █ █▄▀ ▀▀▀▀▀▀▀ \n ██▀▀█ ▀█ ▄█▀▀▀▄█▄█▀▄ \n ▄ ▀ █ ▀██▀█▄▀▄█ ▀ ▀▀ \n ▀▀▀▀ ▀▀▀ ▄▄██ ▀ ▀ \n █▀▀▀▀▀█ ▀▀ ▀▀▄█▄█▀ ▀▀ \n █ ███ █ █▄█ ▀▄ █ ▀ ▀ \n █ ▀▀▀ █ █▄▀▄█ ▀ ▀█ █▀ \n ▀▀▀▀▀▀▀ ▀▀▀ ▀ ▀▀▀ \n \n "; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(invert: true); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_small_ascii_qrcode_with_custom_eol() + { + var targetCode = "█████████████████████████████\r\n█████████████████████████████\r\n████ ▄▄▄▄▄ █▀▄█ ▀█ ▄▄▄▄▄ ████\r\n████ █ █ █▄█ █▄█ █ █ ████\r\n████ █▄▄▄█ █▄▀▀▀▀█ █▄▄▄█ ████\r\n████▄▄▄▄▄▄▄█ █ ▀▄█▄▄▄▄▄▄▄████\r\n████ ▄▄ █▄ ██▀ ▄▄▄▀ ▀ ▄▀████\r\n████▀█▄█ █▄ ▄ ▀▄▀ █▄█▄▄█████\r\n█████▄▄▄▄█▄▄▄████▀▀ █▄█▄████\r\n████ ▄▄▄▄▄ █▄▄█▄▄▀ ▀ ▄█▄▄████\r\n████ █ █ █ ▀ █▄▀█ ██▄█▄████\r\n████ █▄▄▄█ █ ▀▄▀ █▄█▄ █ ▄████\r\n████▄▄▄▄▄▄▄█▄▄▄█████▄█▄▄▄████\r\n█████████████████████████████\r\n▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphicSmall(endOfLine: "\r\n"); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_ascii_qrcode_without_quietzones() + { + var targetCode = "██████████████ ████ ██ ██████████████\n██ ██ ████ ██ ██ ██\n██ ██████ ██ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██ ██ ██ ██████ ██\n██ ██████ ██ ██████████ ██ ██████ ██\n██ ██ ██ ██\n██████████████ ██ ██ ██ ██████████████\n ██████████ \n ████ ██ ████ ██████ ██ ██████████\n██ ██ ██ ██ ██ ████\n ████ ██████ ██████ ██████ ██\n████ ██ ██████ ██ ██ ██ \n ████ ████ ██ ██ ██ ██ ████ \n ██ ██ ██ ██ ██ \n██████████████ ██ ████ ██████ ██ \n██ ██ ██ ████ ██████ \n██ ██████ ██ ██████ ████████ ██ ██\n██ ██████ ██ ██ ██ ████ \n██ ██████ ██ ██████ ██ ██ ██\n██ ██ ██ ██ ██ ██████\n██████████████ ██ ██ ██ ██ ██ ██"; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A05", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphic(1, drawQuietZones: false); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_ascii_qrcode_with_custom_symbols() + { + var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; + + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("A", QRCodeGenerator.ECCLevel.Q); + var asciiCode = new AsciiQRCode(data).GetGraphic(2, "X", " "); + + asciiCode.ShouldBe(targetCode); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_instantate_parameterless() + { + var asciiCode = new AsciiQRCode(); + asciiCode.ShouldNotBeNull(); + asciiCode.ShouldBeOfType(); + } + + [Fact] + [Category("QRRenderer/AsciiQRCode")] + public void can_render_ascii_qrcode_from_helper() + { + var targetCode = " \n \n \n \n \n \n \n \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XXXXXXXXXXXXXX \n XX XX XXXX XX XX \n XX XX XXXX XX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XXXXXX XX XXXX XX XX XXXXXX XX \n XX XX XX XX XX XX \n XX XX XX XX XX XX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XXXXXXXXXXXXXX XX XX XX XXXXXXXXXXXXXX \n XX XX \n XX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XX XX XX XXXXXX XXXX XXXX XX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XX XXXX XX XX XX XXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XXXXXX XXXXXXXXXX XXXXXXXXXX \n XX XX XX XX XX XXXXXX XX XX \n XX XX XX XX XX XXXXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXX XXXX XX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XXXXXXXXXXXXXX XXXXXX XX \n XX XX XX XX XX \n XX XX XX XX XX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XXXXXXXXXX XXXXXXXXXXXXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XX XXXX XX XX XXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XXXXXX XX XXXXXX XXXXXXXXXX \n XX XX XX XXXX XX XX XX \n XX XX XX XXXX XX XX XX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n XXXXXXXXXXXXXX XX XXXXXX XXXXXX \n \n \n \n \n \n \n \n "; + + //Create QR code + var asciiCode = AsciiQRCodeHelper.GetQRCode("A", 2, "X", " ", QRCodeGenerator.ECCLevel.Q); + asciiCode.ShouldBe(targetCode); } } diff --git a/QRCoderTests/Base64QRCodeRendererTests.cs b/QRCoderTests/Base64QRCodeRendererTests.cs index bee20b36..4bf011de 100644 --- a/QRCoderTests/Base64QRCodeRendererTests.cs +++ b/QRCoderTests/Base64QRCodeRendererTests.cs @@ -1,77 +1,76 @@ -#if !NETCOREAPP1_1 -using QRCoder; -using QRCoderTests.Helpers.XUnitExtenstions; -using Shouldly; +#if !NETCOREAPP1_1 using System; using System.Drawing; using System.IO; +using QRCoder; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; using Xunit; -namespace QRCoderTests +namespace QRCoderTests; + +public class Base64QRCodeRendererTests { - public class Base64QRCodeRendererTests - { - private readonly QRCodeData data; + private readonly QRCodeData _data; - public Base64QRCodeRendererTests() - { - var gen = new QRCodeGenerator(); - data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - } + public Base64QRCodeRendererTests() + { + var gen = new QRCodeGenerator(); + _data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + } - [Fact] - [Category("QRRenderer/Base64QRCode")] - public void can_render_base64_qrcode_blackwhite() - { - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5); - var base64QRCode = new Base64QRCode(data).GetGraphic(5); - base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); - } + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_blackwhite() + { + var pngCodeGfx = new PngByteQRCode(_data).GetGraphic(5); + var base64QRCode = new Base64QRCode(_data).GetGraphic(5); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } - [Fact] - [Category("QRRenderer/Base64QRCode")] - public void can_render_base64_qrcode_noquietzones() - { - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, false); - var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Black, Color.White, false); - base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); - } + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_noquietzones() + { + var pngCodeGfx = new PngByteQRCode(_data).GetGraphic(5, false); + var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Black, Color.White, false); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } - [Fact] - [Category("QRRenderer/Base64QRCode")] - public void can_render_base64_qrcode_color() - { - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); - var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Red, Color.Blue); - base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); - } + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_color() + { + var pngCodeGfx = new PngByteQRCode(_data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); + var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Red, Color.Blue); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } - [Fact] - [Category("QRRenderer/Base64QRCode")] - public void can_render_base64_qrcode_transparent() - { - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 0, 255, 0, 255 }, new byte[] { 255, 255, 255, 0 }); - var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Lime, Color.Transparent); - base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); - } + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_transparent() + { + var pngCodeGfx = new PngByteQRCode(_data).GetGraphic(5, new byte[] { 0, 255, 0, 255 }, new byte[] { 255, 255, 255, 0 }); + var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Lime, Color.Transparent); + base64QRCode.ShouldBe(Convert.ToBase64String(pngCodeGfx)); + } #if SYSTEM_DRAWING - [Fact] - [Category("QRRenderer/Base64QRCode")] - public void can_render_base64_qrcode_jpeg() + [Fact] + [Category("QRRenderer/Base64QRCode")] + public void can_render_base64_qrcode_jpeg() + { + var ms = new MemoryStream(); + using (var bitmap = new QRCode(_data).GetGraphic(5, Color.Black, Color.White, true)) { - var ms = new MemoryStream(); - using (var bitmap = new QRCode(data).GetGraphic(5, Color.Black, Color.White, true)) - { - bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); - } - ms.Position = 0; - var jpgString = Convert.ToBase64String(ms.ToArray()); - var base64QRCode = new Base64QRCode(data).GetGraphic(5, Color.Black, Color.White, true, Base64QRCode.ImageType.Jpeg); - base64QRCode.ShouldBe(jpgString); + bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); } -#endif + ms.Position = 0; + var jpgString = Convert.ToBase64String(ms.ToArray()); + var base64QRCode = new Base64QRCode(_data).GetGraphic(5, Color.Black, Color.White, true, Base64QRCode.ImageType.Jpeg); + base64QRCode.ShouldBe(jpgString); } +#endif } #endif diff --git a/QRCoderTests/Helpers/CategoryDiscoverer.cs b/QRCoderTests/Helpers/CategoryDiscoverer.cs index 758617c9..f67c3662 100644 --- a/QRCoderTests/Helpers/CategoryDiscoverer.cs +++ b/QRCoderTests/Helpers/CategoryDiscoverer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; #if !NETFRAMEWORK @@ -6,32 +6,35 @@ #endif using Xunit.Sdk; -namespace QRCoderTests.Helpers.XUnitExtenstions -{ +namespace QRCoderTests.Helpers.XUnitExtenstions; + #if NETFRAMEWORK - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class CategoryAttribute : Attribute - { - public CategoryAttribute(string category) { } - } +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CategoryAttribute : Attribute +{ +#pragma warning disable IDE0060 // Remove unused parameter + public CategoryAttribute(string category) { } +#pragma warning restore IDE0060 // Remove unused parameter +} #else - public class CategoryDiscoverer : ITraitDiscoverer - { - public const string KEY = "Category"; - - public IEnumerable> GetTraits(IAttributeInfo traitAttribute) - { - var ctorArgs = traitAttribute.GetConstructorArguments().ToList(); - yield return new KeyValuePair(KEY, ctorArgs[0].ToString()); - } - } +public class CategoryDiscoverer : ITraitDiscoverer +{ + public const string KEY = "Category"; - //NOTICE: Take a note that you must provide appropriate namespace here - [TraitDiscoverer("QRCoderTests.XUnitExtenstions.CategoryDiscoverer", "QRCoderTests")] - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - public class CategoryAttribute : Attribute, ITraitAttribute + public IEnumerable> GetTraits(IAttributeInfo traitAttribute) { - public CategoryAttribute(string category) { } + var ctorArgs = traitAttribute.GetConstructorArguments().ToList(); + yield return new KeyValuePair(KEY, ctorArgs[0].ToString()); } -#endif } + +//NOTICE: Take a note that you must provide appropriate namespace here +[TraitDiscoverer("QRCoderTests.XUnitExtenstions.CategoryDiscoverer", "QRCoderTests")] +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class CategoryAttribute : Attribute, ITraitAttribute +{ +#pragma warning disable IDE0060 // Remove unused parameter + public CategoryAttribute(string category) { } +#pragma warning restore IDE0060 // Remove unused parameter +} +#endif diff --git a/QRCoderTests/Helpers/HelperFunctions.cs b/QRCoderTests/Helpers/HelperFunctions.cs index c6bf59ec..58e55a4e 100644 --- a/QRCoderTests/Helpers/HelperFunctions.cs +++ b/QRCoderTests/Helpers/HelperFunctions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text; using System.IO; using System.Security.Cryptography; @@ -13,102 +13,92 @@ #endif -namespace QRCoderTests.Helpers +namespace QRCoderTests.Helpers; + +public static class HelperFunctions { - public static class HelperFunctions - { #if TEST_XAML - public static BitmapSource ToBitmapSource(DrawingImage source) - { - DrawingVisual drawingVisual = new DrawingVisual(); - DrawingContext drawingContext = drawingVisual.RenderOpen(); - drawingContext.DrawImage(source, new SW.Rect(new SW.Point(0, 0), new SW.Size(source.Width, source.Height))); - drawingContext.Close(); + public static BitmapSource ToBitmapSource(DrawingImage source) + { + var drawingVisual = new DrawingVisual(); + var drawingContext = drawingVisual.RenderOpen(); + drawingContext.DrawImage(source, new SW.Rect(new SW.Point(0, 0), new SW.Size(source.Width, source.Height))); + drawingContext.Close(); - RenderTargetBitmap bmp = new RenderTargetBitmap((int)source.Width, (int)source.Height, 96, 96, PixelFormats.Pbgra32); - bmp.Render(drawingVisual); - return bmp; - } + var bmp = new RenderTargetBitmap((int)source.Width, (int)source.Height, 96, 96, PixelFormats.Pbgra32); + bmp.Render(drawingVisual); + return bmp; + } - public static Bitmap BitmapSourceToBitmap(DrawingImage xamlImg) - { - using (MemoryStream ms = new MemoryStream()) - { - PngBitmapEncoder encoder = new PngBitmapEncoder(); - encoder.Frames.Add(BitmapFrame.Create(ToBitmapSource(xamlImg))); - encoder.Save(ms); + public static Bitmap BitmapSourceToBitmap(DrawingImage xamlImg) + { + using var ms = new MemoryStream(); + var encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(ToBitmapSource(xamlImg))); + encoder.Save(ms); - using (Bitmap bmp = new Bitmap(ms)) - { - return new Bitmap(bmp); - } - } - } + using var bmp = new Bitmap(ms); + return new Bitmap(bmp); + } #endif - public static string GetAssemblyPath() - { - return + public static string GetAssemblyPath() #if NET5_0_OR_GREATER - AppDomain.CurrentDomain.BaseDirectory; + => AppDomain.CurrentDomain.BaseDirectory; #elif NETFRAMEWORK - Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).Replace("file:\\", ""); + => Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).Replace("file:\\", ""); #elif NETCOREAPP1_1 - Path.GetDirectoryName(typeof(HelperFunctions).GetTypeInfo().Assembly.Location).Replace("file:\\", ""); + => Path.GetDirectoryName(typeof(HelperFunctions).GetTypeInfo().Assembly.Location).Replace("file:\\", ""); #else - Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location).Replace("file:\\", ""); + => Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location).Replace("file:\\", ""); #endif - } #if !NETCOREAPP1_1 - /// - /// Converts a bitmap to a hash string based on the pixel data - /// using a deterministic algorithm that ignores compression algorithm - /// differences across platforms. - /// - public static string BitmapToHash(Bitmap bitmap) - { - // Lock the bitmap's bits. - var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); - var bitmapData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - byte[] rgbValues; - try - { - // Create an array to hold the bytes of the bitmap. - int bytes = Math.Abs(bitmapData.Stride) * bitmap.Height; - rgbValues = new byte[bytes]; + /// + /// Converts a bitmap to a hash string based on the pixel data + /// using a deterministic algorithm that ignores compression algorithm + /// differences across platforms. + /// + public static string BitmapToHash(Bitmap bitmap) + { + // Lock the bitmap's bits. + var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); + var bitmapData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - // Copy the RGB values into the array. - System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, rgbValues, 0, bytes); - } - finally - { - // Unlock the bits. - bitmap.UnlockBits(bitmapData); - } + byte[] rgbValues; + try + { + // Create an array to hold the bytes of the bitmap. + int bytes = Math.Abs(bitmapData.Stride) * bitmap.Height; + rgbValues = new byte[bytes]; - // Hash the resulting byte array - return ByteArrayToHash(rgbValues); + // Copy the RGB values into the array. + System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, rgbValues, 0, bytes); + } + finally + { + // Unlock the bits. + bitmap.UnlockBits(bitmapData); } + + // Hash the resulting byte array + return ByteArrayToHash(rgbValues); + } #endif - public static string ByteArrayToHash(byte[] data) - { + public static string ByteArrayToHash(byte[] data) + { #if !NETCOREAPP1_1 - var md5 = MD5.Create(); - var hash = md5.ComputeHash(data); + var md5 = MD5.Create(); + var hash = md5.ComputeHash(data); #else - var hash = new SshNet.Security.Cryptography.MD5().ComputeHash(data); + var hash = new SshNet.Security.Cryptography.MD5().ComputeHash(data); #endif - return BitConverter.ToString(hash).Replace("-", "").ToLower(); - } - - public static string StringToHash(string data) - { - return ByteArrayToHash(Encoding.UTF8.GetBytes(data)); - } + return BitConverter.ToString(hash).Replace("-", "").ToLower(); } + + public static string StringToHash(string data) + => ByteArrayToHash(Encoding.UTF8.GetBytes(data)); } diff --git a/QRCoderTests/PayloadGeneratorTests.cs b/QRCoderTests/PayloadGeneratorTests.cs index 17931b95..30fee18c 100644 --- a/QRCoderTests/PayloadGeneratorTests.cs +++ b/QRCoderTests/PayloadGeneratorTests.cs @@ -1,3592 +1,3590 @@ -using System; -using Xunit; -using QRCoder; -using Shouldly; +using System; using System.Globalization; +using System.Reflection; using System.Threading; +using QRCoder; using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; using static QRCoder.PayloadGenerator.BezahlCode; -using static QRCoder.PayloadGenerator.SwissQrCode.Reference; -using System.Reflection; using static QRCoder.PayloadGenerator.SwissQrCode.AdditionalInformation; +using static QRCoder.PayloadGenerator.SwissQrCode.Reference; using static QRCoder.QRCodeGenerator; -namespace QRCoderTests +namespace QRCoderTests; + +public class PayloadGeneratorTests { - public class PayloadGeneratorTests + [Fact] + [Category("PayloadGenerator/BitcoinAddress")] + public void bitcoin_address_generator_can_generate_address() { + var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; + var amount = .123; + var label = "Some Label to Encode"; + var message = "Some Message to Encode"; - [Fact] - [Category("PayloadGenerator/BitcoinAddress")] - public void bitcoin_address_generator_can_generate_address() - { - var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; - var amount = .123; - var label = "Some Label to Encode"; - var message = "Some Message to Encode"; - - var generator = new PayloadGenerator.BitcoinAddress(address, amount, label, message); + var generator = new PayloadGenerator.BitcoinAddress(address, amount, label, message); - generator - .ToString() - .ShouldBe("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); - } + generator + .ToString() + .ShouldBe("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); + } - [Fact] - [Category("PayloadGenerator/BitcoinAddress")] - public void bitcoin_address_generator_should_skip_missing_label() - { - var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; - var amount = .123; - var message = "Some Message to Encode"; + [Fact] + [Category("PayloadGenerator/BitcoinAddress")] + public void bitcoin_address_generator_should_skip_missing_label() + { + var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; + var amount = .123; + var message = "Some Message to Encode"; - var generator = new PayloadGenerator.BitcoinAddress(address, amount, null, message); + var generator = new PayloadGenerator.BitcoinAddress(address, amount, null, message); - generator - .ToString() - .ShouldNotContain("label"); - } + generator + .ToString() + .ShouldNotContain("label"); + } - [Fact] - [Category("PayloadGenerator/BitcoinAddress")] - public void bitcoin_address_generator_should_skip_missing_message() - { - var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; - var amount = .123; + [Fact] + [Category("PayloadGenerator/BitcoinAddress")] + public void bitcoin_address_generator_should_skip_missing_message() + { + var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; + var amount = .123; - var generator = new PayloadGenerator.BitcoinAddress(address, amount); + var generator = new PayloadGenerator.BitcoinAddress(address, amount); - generator - .ToString() - .ShouldNotContain("message"); - } + generator + .ToString() + .ShouldNotContain("message"); + } - [Fact] - [Category("PayloadGenerator/BitcoinAddress")] - public void bitcoin_address_generator_should_round_to_satoshi() - { - var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; - var amount = .123456789; + [Fact] + [Category("PayloadGenerator/BitcoinAddress")] + public void bitcoin_address_generator_should_round_to_satoshi() + { + var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; + var amount = .123456789; - var generator = new PayloadGenerator.BitcoinAddress(address, amount); + var generator = new PayloadGenerator.BitcoinAddress(address, amount); - generator - .ToString() - .ShouldContain("amount=.12345679"); - } + generator + .ToString() + .ShouldContain("amount=.12345679"); + } - [Fact] - [Category("PayloadGenerator/BitcoinAddress")] - public void bitcoin_address_generator_disregards_current_culture() - { + [Fact] + [Category("PayloadGenerator/BitcoinAddress")] + public void bitcoin_address_generator_disregards_current_culture() + { #if NETCOREAPP1_1 - var currentCulture = CultureInfo.DefaultThreadCurrentCulture; - CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); + var currentCulture = CultureInfo.DefaultThreadCurrentCulture; + CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); #else - var currentCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); + var currentCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); #endif - var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; - var amount = .123; + var address = "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"; + var amount = .123; - var generator = new PayloadGenerator.BitcoinAddress(address, amount); + var generator = new PayloadGenerator.BitcoinAddress(address, amount); - generator - .ToString() - .ShouldBe("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=.123"); + generator + .ToString() + .ShouldBe("bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=.123"); #if NETCOREAPP1_1 - CultureInfo.DefaultThreadCurrentCulture = currentCulture; + CultureInfo.DefaultThreadCurrentCulture = currentCulture; #else - Thread.CurrentThread.CurrentCulture = currentCulture; + Thread.CurrentThread.CurrentCulture = currentCulture; #endif - } + } - [Fact] - [Category("PayloadGenerator/BitcoinCashAddress")] - public void bitcoincash_address_generator_can_generate_address() - { - var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; - var amount = .123; - var label = "Some Label to Encode"; - var message = "Some Message to Encode"; + [Fact] + [Category("PayloadGenerator/BitcoinCashAddress")] + public void bitcoincash_address_generator_can_generate_address() + { + var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; + var amount = .123; + var label = "Some Label to Encode"; + var message = "Some Message to Encode"; - var generator = new PayloadGenerator.BitcoinCashAddress(address, amount, label, message); + var generator = new PayloadGenerator.BitcoinCashAddress(address, amount, label, message); - generator - .ToString() - .ShouldBe("bitcoincash:qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); - } + generator + .ToString() + .ShouldBe("bitcoincash:qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); + } - [Fact] - [Category("PayloadGenerator/BitcoinCashAddress")] - public void bitcoincash_address_generator_should_skip_missing_label() - { - var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; - var amount = .123; - var message = "Some Message to Encode"; + [Fact] + [Category("PayloadGenerator/BitcoinCashAddress")] + public void bitcoincash_address_generator_should_skip_missing_label() + { + var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; + var amount = .123; + var message = "Some Message to Encode"; - var generator = new PayloadGenerator.BitcoinCashAddress(address, amount, null, message); + var generator = new PayloadGenerator.BitcoinCashAddress(address, amount, null, message); - generator - .ToString() - .ShouldNotContain("label"); - } + generator + .ToString() + .ShouldNotContain("label"); + } - [Fact] - [Category("PayloadGenerator/BitcoinCashAddress")] - public void bitcoincash_address_generator_should_skip_missing_message() - { - var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; - var amount = .123; + [Fact] + [Category("PayloadGenerator/BitcoinCashAddress")] + public void bitcoincash_address_generator_should_skip_missing_message() + { + var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; + var amount = .123; - var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); + var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); - generator - .ToString() - .ShouldNotContain("message"); - } + generator + .ToString() + .ShouldNotContain("message"); + } - [Fact] - [Category("PayloadGenerator/BitcoinCashAddress")] - public void bitcoincash_address_generator_should_round_to_satoshi() - { - var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; - var amount = .123456789; + [Fact] + [Category("PayloadGenerator/BitcoinCashAddress")] + public void bitcoincash_address_generator_should_round_to_satoshi() + { + var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; + var amount = .123456789; - var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); + var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); - generator - .ToString() - .ShouldContain("amount=.12345679"); - } + generator + .ToString() + .ShouldContain("amount=.12345679"); + } - [Fact] - [Category("PayloadGenerator/BitcoinCashAddress")] - public void bitcoincash_address_generator_disregards_current_culture() - { + [Fact] + [Category("PayloadGenerator/BitcoinCashAddress")] + public void bitcoincash_address_generator_disregards_current_culture() + { #if NETCOREAPP1_1 - var currentCulture = CultureInfo.DefaultThreadCurrentCulture; - CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); + var currentCulture = CultureInfo.DefaultThreadCurrentCulture; + CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); #else - var currentCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); + var currentCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); #endif - var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; - var amount = .123; + var address = "qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890"; + var amount = .123; - var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); + var generator = new PayloadGenerator.BitcoinCashAddress(address, amount); - generator - .ToString() - .ShouldBe("bitcoincash:qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890?amount=.123"); + generator + .ToString() + .ShouldBe("bitcoincash:qqtlfk37qyey50f4wfuhc7jw85zsdp8s2swffjk890?amount=.123"); #if NETCOREAPP1_1 - CultureInfo.DefaultThreadCurrentCulture = currentCulture; + CultureInfo.DefaultThreadCurrentCulture = currentCulture; #else - Thread.CurrentThread.CurrentCulture = currentCulture; + Thread.CurrentThread.CurrentCulture = currentCulture; #endif - } + } - [Fact] - [Category("PayloadGenerator/LitecoinAddress")] - public void litecoin_address_generator_can_generate_address() - { - var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; - var amount = .123; - var label = "Some Label to Encode"; - var message = "Some Message to Encode"; + [Fact] + [Category("PayloadGenerator/LitecoinAddress")] + public void litecoin_address_generator_can_generate_address() + { + var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; + var amount = .123; + var label = "Some Label to Encode"; + var message = "Some Message to Encode"; - var generator = new PayloadGenerator.LitecoinAddress(address, amount, label, message); + var generator = new PayloadGenerator.LitecoinAddress(address, amount, label, message); - generator - .ToString() - .ShouldBe("litecoin:LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); - } + generator + .ToString() + .ShouldBe("litecoin:LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54?label=Some%20Label%20to%20Encode&message=Some%20Message%20to%20Encode&amount=.123"); + } - [Fact] - [Category("PayloadGenerator/LitecoinAddress")] - public void litecoin_address_generator_should_skip_missing_label() - { - var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; - var amount = .123; - var message = "Some Message to Encode"; + [Fact] + [Category("PayloadGenerator/LitecoinAddress")] + public void litecoin_address_generator_should_skip_missing_label() + { + var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; + var amount = .123; + var message = "Some Message to Encode"; - var generator = new PayloadGenerator.LitecoinAddress(address, amount, null, message); + var generator = new PayloadGenerator.LitecoinAddress(address, amount, null, message); - generator - .ToString() - .ShouldNotContain("label"); - } + generator + .ToString() + .ShouldNotContain("label"); + } - [Fact] - [Category("PayloadGenerator/LitecoinAddress")] - public void litecoin_address_generator_should_skip_missing_message() - { - var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; - var amount = .123; + [Fact] + [Category("PayloadGenerator/LitecoinAddress")] + public void litecoin_address_generator_should_skip_missing_message() + { + var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; + var amount = .123; - var generator = new PayloadGenerator.LitecoinAddress(address, amount); + var generator = new PayloadGenerator.LitecoinAddress(address, amount); - generator - .ToString() - .ShouldNotContain("message"); - } + generator + .ToString() + .ShouldNotContain("message"); + } - [Fact] - [Category("PayloadGenerator/LitecoinAddress")] - public void litecoin_address_generator_should_round_to_satoshi() - { - var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; - var amount = .123456789; + [Fact] + [Category("PayloadGenerator/LitecoinAddress")] + public void litecoin_address_generator_should_round_to_satoshi() + { + var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; + var amount = .123456789; - var generator = new PayloadGenerator.LitecoinAddress(address, amount); + var generator = new PayloadGenerator.LitecoinAddress(address, amount); - generator - .ToString() - .ShouldContain("amount=.12345679"); - } + generator + .ToString() + .ShouldContain("amount=.12345679"); + } - [Fact] - [Category("PayloadGenerator/LitecoinAddress")] - public void litecoin_address_generator_disregards_current_culture() - { + [Fact] + [Category("PayloadGenerator/LitecoinAddress")] + public void litecoin_address_generator_disregards_current_culture() + { #if NETCOREAPP1_1 - var currentCulture = CultureInfo.DefaultThreadCurrentCulture; - CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); + var currentCulture = CultureInfo.DefaultThreadCurrentCulture; + CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); #else - var currentCulture = Thread.CurrentThread.CurrentCulture; - Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); + var currentCulture = Thread.CurrentThread.CurrentCulture; + Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE"); #endif - var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; - var amount = .123; + var address = "LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54"; + var amount = .123; - var generator = new PayloadGenerator.LitecoinAddress(address, amount); + var generator = new PayloadGenerator.LitecoinAddress(address, amount); - generator - .ToString() - .ShouldBe("litecoin:LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54?amount=.123"); + generator + .ToString() + .ShouldBe("litecoin:LY1t7iLnwtPCb1DPZP38FA835XzFqXBq54?amount=.123"); #if NETCOREAPP1_1 - CultureInfo.DefaultThreadCurrentCulture = currentCulture; + CultureInfo.DefaultThreadCurrentCulture = currentCulture; #else - Thread.CurrentThread.CurrentCulture = currentCulture; + Thread.CurrentThread.CurrentCulture = currentCulture; #endif - } + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_build_wep() - { - var ssid = "MyWiFiSSID"; - var password = "7heP4assw0rd"; - var authmode = PayloadGenerator.WiFi.Authentication.WEP; - var hideSSID = false; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_build_wep() + { + var ssid = "MyWiFiSSID"; + var password = "7heP4assw0rd"; + var authmode = PayloadGenerator.WiFi.Authentication.WEP; + var hideSSID = false; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WEP;S:MyWiFiSSID;P:7heP4assw0rd;;"); - } + generator.ToString().ShouldBe($"WIFI:T:WEP;S:MyWiFiSSID;P:7heP4assw0rd;;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_build_wpa() - { - var ssid = "MyWiFiSSID"; - var password = "7heP4assw0rd"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = false; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_build_wpa() + { + var ssid = "MyWiFiSSID"; + var password = "7heP4assw0rd"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = false; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_build_wpa2() - { - var ssid = "MyWiFiSSID"; - var password = "7heP4assw0rd"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA2; - var hideSSID = false; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_build_wpa2() + { + var ssid = "MyWiFiSSID"; + var password = "7heP4assw0rd"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA2; + var hideSSID = false; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA2;S:MyWiFiSSID;P:7heP4assw0rd;;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA2;S:MyWiFiSSID;P:7heP4assw0rd;;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_ignore_hiddenSSID_param() - { - var ssid = "MyWiFiSSID"; - var password = "7heP4assw0rd"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_ignore_hiddenSSID_param() + { + var ssid = "MyWiFiSSID"; + var password = "7heP4assw0rd"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_add_hiddenSSID_param() - { - var ssid = "M\\y;W,i:FiSSID"; - var password = "7heP4assw0rd\\;:,"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_add_hiddenSSID_param() + { + var ssid = "M\\y;W,i:FiSSID"; + var password = "7heP4assw0rd\\;:,"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:M\\\\y\\;W\\,i\\:FiSSID;P:7heP4assw0rd\\\\\\;\\:\\,;H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:M\\\\y\\;W\\,i\\:FiSSID;P:7heP4assw0rd\\\\\\;\\:\\,;H:true;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_escape_input() - { - var ssid = "MyWiFiSSID"; - var password = "7heP4assw0rd"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_escape_input() + { + var ssid = "MyWiFiSSID"; + var password = "7heP4assw0rd"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:MyWiFiSSID;P:7heP4assw0rd;H:true;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_escape_hex_style1() - { - var ssid = "A9B7F18CCE"; - var password = "00105F0E6"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_escape_hex_style1() + { + var ssid = "A9B7F18CCE"; + var password = "00105F0E6"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"A9B7F18CCE\";P:\"00105F0E6\";H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"A9B7F18CCE\";P:\"00105F0E6\";H:true;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_escape_hex_style2() - { - var ssid = "a9b7f18cce"; - var password = "00105f0Ee6"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_escape_hex_style2() + { + var ssid = "a9b7f18cce"; + var password = "00105f0Ee6"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"a9b7f18cce\";P:\"00105f0Ee6\";H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"a9b7f18cce\";P:\"00105f0Ee6\";H:true;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_escape_hex_style3() - { - var ssid = "0xA9B7F18CCE"; - var password = "0x00105F0E6"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_escape_hex_style3() + { + var ssid = "0xA9B7F18CCE"; + var password = "0x00105F0E6"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"0xA9B7F18CCE\";P:\"0x00105F0E6\";H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"0xA9B7F18CCE\";P:\"0x00105F0E6\";H:true;"); + } - [Fact] - [Category("PayloadGenerator/WiFi")] - public void wifi_should_escape_hex_style4() - { - var ssid = "0XA9B7F18CCE"; - var password = "0X00105F0E6"; - var authmode = PayloadGenerator.WiFi.Authentication.WPA; - var hideSSID = true; + [Fact] + [Category("PayloadGenerator/WiFi")] + public void wifi_should_escape_hex_style4() + { + var ssid = "0XA9B7F18CCE"; + var password = "0X00105F0E6"; + var authmode = PayloadGenerator.WiFi.Authentication.WPA; + var hideSSID = true; - var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); + var generator = new PayloadGenerator.WiFi(ssid, password, authmode, hideSSID); - generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"0XA9B7F18CCE\";P:\"0X00105F0E6\";H:true;"); - } + generator.ToString().ShouldBe($"WIFI:T:WPA;S:\"0XA9B7F18CCE\";P:\"0X00105F0E6\";H:true;"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_mailto() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var message = "Just see if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_mailto() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var message = "Just see if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; - var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); - generator.ToString().ShouldBe("mailto:john@doe.com?subject=A%20test%20mail&body=Just%20see%20if%20it%20works%21"); - } + generator.ToString().ShouldBe("mailto:john@doe.com?subject=A%20test%20mail&body=Just%20see%20if%20it%20works%21"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_mailto_receiver_only() - { - var receiver = "john@doe.com"; - var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_mailto_receiver_only() + { + var receiver = "john@doe.com"; + var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; - var generator = new PayloadGenerator.Mail(mailReceiver: receiver, encoding: encoding); + var generator = new PayloadGenerator.Mail(mailReceiver: receiver, encoding: encoding); - generator.ToString().ShouldBe("mailto:john@doe.com"); - } + generator.ToString().ShouldBe("mailto:john@doe.com"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_mailto_subject_only() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_mailto_subject_only() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; - var generator = new PayloadGenerator.Mail(receiver, subject, encoding: encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, encoding: encoding); - generator.ToString().ShouldBe("mailto:john@doe.com?subject=A%20test%20mail"); - } + generator.ToString().ShouldBe("mailto:john@doe.com?subject=A%20test%20mail"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_mailto_message_only() - { - var receiver = "john@doe.com"; - var message = "Just see if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_mailto_message_only() + { + var receiver = "john@doe.com"; + var message = "Just see if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; - var generator = new PayloadGenerator.Mail(receiver, message: message, encoding: encoding); + var generator = new PayloadGenerator.Mail(receiver, message: message, encoding: encoding); - generator.ToString().ShouldBe("mailto:john@doe.com?body=Just%20see%20if%20it%20works%21"); - } + generator.ToString().ShouldBe("mailto:john@doe.com?body=Just%20see%20if%20it%20works%21"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_mailto_no_receiver() - { - var subject = "A test mail"; - var message = "Just see if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_mailto_no_receiver() + { + var subject = "A test mail"; + var message = "Just see if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.MAILTO; - var generator = new PayloadGenerator.Mail(subject: subject, message: message, encoding: encoding); + var generator = new PayloadGenerator.Mail(subject: subject, message: message, encoding: encoding); - generator.ToString().ShouldBe("mailto:?subject=A%20test%20mail&body=Just%20see%20if%20it%20works%21"); - } + generator.ToString().ShouldBe("mailto:?subject=A%20test%20mail&body=Just%20see%20if%20it%20works%21"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_MATMSG() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var message = "Just see if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.MATMSG; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_MATMSG() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var message = "Just see if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.MATMSG; - var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); - generator.ToString().ShouldBe("MATMSG:TO:john@doe.com;SUB:A test mail;BODY:Just see if it works!;;"); - } + generator.ToString().ShouldBe("MATMSG:TO:john@doe.com;SUB:A test mail;BODY:Just see if it works!;;"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_build_type_SMTP() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var message = "Just see if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.SMTP; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_build_type_SMTP() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var message = "Just see if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.SMTP; - var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); - generator.ToString().ShouldBe("SMTP:john@doe.com:A test mail:Just see if it works!"); - } + generator.ToString().ShouldBe("SMTP:john@doe.com:A test mail:Just see if it works!"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_escape_input_MATMSG() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var message = "Just see if \\:;, it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.MATMSG; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_escape_input_MATMSG() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var message = "Just see if \\:;, it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.MATMSG; - var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); - generator.ToString().ShouldBe("MATMSG:TO:john@doe.com;SUB:A test mail;BODY:Just see if \\\\\\:\\;\\, it works!;;"); - } + generator.ToString().ShouldBe("MATMSG:TO:john@doe.com;SUB:A test mail;BODY:Just see if \\\\\\:\\;\\, it works!;;"); + } - [Fact] - [Category("PayloadGenerator/Mail")] - public void mail_should_escape_input_SMTP() - { - var receiver = "john@doe.com"; - var subject = "A test mail"; - var message = "Just see: if it works!"; - var encoding = PayloadGenerator.Mail.MailEncoding.SMTP; + [Fact] + [Category("PayloadGenerator/Mail")] + public void mail_should_escape_input_SMTP() + { + var receiver = "john@doe.com"; + var subject = "A test mail"; + var message = "Just see: if it works!"; + var encoding = PayloadGenerator.Mail.MailEncoding.SMTP; - var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); + var generator = new PayloadGenerator.Mail(receiver, subject, message, encoding); - generator.ToString().ShouldBe("SMTP:john@doe.com:A test mail:Just see\\: if it works!"); - } + generator.ToString().ShouldBe("SMTP:john@doe.com:A test mail:Just see\\: if it works!"); + } - [Fact] - [Category("PayloadGenerator/SMS")] - public void sms_should_build_type_SMS() - { - var number = "01601234567"; - var message = "A small SMS"; - var encoding = PayloadGenerator.SMS.SMSEncoding.SMS; + [Fact] + [Category("PayloadGenerator/SMS")] + public void sms_should_build_type_SMS() + { + var number = "01601234567"; + var message = "A small SMS"; + var encoding = PayloadGenerator.SMS.SMSEncoding.SMS; - var generator = new PayloadGenerator.SMS(number, message, encoding); + var generator = new PayloadGenerator.SMS(number, message, encoding); - generator.ToString().ShouldBe("sms:01601234567?body=A%20small%20SMS"); - } + generator.ToString().ShouldBe("sms:01601234567?body=A%20small%20SMS"); + } - [Fact] - [Category("PayloadGenerator/SMS")] - public void sms_should_build_type_SMS_iOS() - { - var number = "01601234567"; - var message = "A small SMS"; - var encoding = PayloadGenerator.SMS.SMSEncoding.SMS_iOS; + [Fact] + [Category("PayloadGenerator/SMS")] + public void sms_should_build_type_SMS_iOS() + { + var number = "01601234567"; + var message = "A small SMS"; + var encoding = PayloadGenerator.SMS.SMSEncoding.SMS_iOS; - var generator = new PayloadGenerator.SMS(number, message, encoding); + var generator = new PayloadGenerator.SMS(number, message, encoding); - generator.ToString().ShouldBe("sms:01601234567;body=A%20small%20SMS"); - } + generator.ToString().ShouldBe("sms:01601234567;body=A%20small%20SMS"); + } - [Fact] - [Category("PayloadGenerator/SMS")] - public void sms_should_build_type_SMSTO() - { - var number = "01601234567"; - var message = "A small SMS"; - var encoding = PayloadGenerator.SMS.SMSEncoding.SMSTO; + [Fact] + [Category("PayloadGenerator/SMS")] + public void sms_should_build_type_SMSTO() + { + var number = "01601234567"; + var message = "A small SMS"; + var encoding = PayloadGenerator.SMS.SMSEncoding.SMSTO; - var generator = new PayloadGenerator.SMS(number, message, encoding); + var generator = new PayloadGenerator.SMS(number, message, encoding); - generator.ToString().ShouldBe("SMSTO:01601234567:A small SMS"); - } + generator.ToString().ShouldBe("SMSTO:01601234567:A small SMS"); + } - [Fact] - [Category("PayloadGenerator/SMS")] - public void sms_should_not_add_unused_params() - { - var number = "01601234567"; + [Fact] + [Category("PayloadGenerator/SMS")] + public void sms_should_not_add_unused_params() + { + var number = "01601234567"; - var generator = new PayloadGenerator.SMS(number); + var generator = new PayloadGenerator.SMS(number); - generator.ToString().ShouldBe("sms:01601234567"); - } + generator.ToString().ShouldBe("sms:01601234567"); + } - [Fact] - [Category("PayloadGenerator/MMS")] - public void mms_should_build_type_MMS() - { - var number = "01601234567"; - var message = "A tiny MMS"; - var encoding = PayloadGenerator.MMS.MMSEncoding.MMS; + [Fact] + [Category("PayloadGenerator/MMS")] + public void mms_should_build_type_MMS() + { + var number = "01601234567"; + var message = "A tiny MMS"; + var encoding = PayloadGenerator.MMS.MMSEncoding.MMS; - var generator = new PayloadGenerator.MMS(number, message, encoding); + var generator = new PayloadGenerator.MMS(number, message, encoding); - generator.ToString().ShouldBe("mms:01601234567?body=A%20tiny%20MMS"); - } + generator.ToString().ShouldBe("mms:01601234567?body=A%20tiny%20MMS"); + } - [Fact] - [Category("PayloadGenerator/MMS")] - public void mms_should_build_type_MMSTO() - { - var number = "01601234567"; - var message = "A tiny SMS"; - var encoding = PayloadGenerator.MMS.MMSEncoding.MMSTO; + [Fact] + [Category("PayloadGenerator/MMS")] + public void mms_should_build_type_MMSTO() + { + var number = "01601234567"; + var message = "A tiny SMS"; + var encoding = PayloadGenerator.MMS.MMSEncoding.MMSTO; - var generator = new PayloadGenerator.MMS(number, message, encoding); + var generator = new PayloadGenerator.MMS(number, message, encoding); - generator.ToString().ShouldBe("mmsto:01601234567?subject=A%20tiny%20SMS"); - } + generator.ToString().ShouldBe("mmsto:01601234567?subject=A%20tiny%20SMS"); + } - [Fact] - [Category("PayloadGenerator/MMS")] - public void mms_should_not_add_unused_params() - { - var number = "01601234567"; + [Fact] + [Category("PayloadGenerator/MMS")] + public void mms_should_not_add_unused_params() + { + var number = "01601234567"; - var generator = new PayloadGenerator.MMS(number); + var generator = new PayloadGenerator.MMS(number); - generator.ToString().ShouldBe("mms:01601234567"); - } + generator.ToString().ShouldBe("mms:01601234567"); + } - [Fact] - [Category("PayloadGenerator/Geolocation")] - public void geolocation_should_build_type_GEO() - { - var latitude = "51.227741"; - var longitude = "6.773456"; - var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GEO; + [Fact] + [Category("PayloadGenerator/Geolocation")] + public void geolocation_should_build_type_GEO() + { + var latitude = "51.227741"; + var longitude = "6.773456"; + var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GEO; - var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); + var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); - generator.ToString().ShouldBe("geo:51.227741,6.773456"); - } + generator.ToString().ShouldBe("geo:51.227741,6.773456"); + } - [Fact] - [Category("PayloadGenerator/Geolocation")] - public void geolocation_should_build_type_GoogleMaps() - { - var latitude = "51.227741"; - var longitude = "6.773456"; - var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GoogleMaps; + [Fact] + [Category("PayloadGenerator/Geolocation")] + public void geolocation_should_build_type_GoogleMaps() + { + var latitude = "51.227741"; + var longitude = "6.773456"; + var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GoogleMaps; - var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); + var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); - generator.ToString().ShouldBe("http://maps.google.com/maps?q=51.227741,6.773456"); - } + generator.ToString().ShouldBe("http://maps.google.com/maps?q=51.227741,6.773456"); + } - [Fact] - [Category("PayloadGenerator/Geolocation")] - public void geolocation_should_escape_input() - { - var latitude = "51,227741"; - var longitude = "6,773456"; - var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GEO; + [Fact] + [Category("PayloadGenerator/Geolocation")] + public void geolocation_should_escape_input() + { + var latitude = "51,227741"; + var longitude = "6,773456"; + var encoding = PayloadGenerator.Geolocation.GeolocationEncoding.GEO; - var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); + var generator = new PayloadGenerator.Geolocation(latitude, longitude, encoding); - generator.ToString().ShouldBe("geo:51.227741,6.773456"); - } + generator.ToString().ShouldBe("geo:51.227741,6.773456"); + } - [Fact] - [Category("PayloadGenerator/Geolocation")] - public void geolocation_should_add_unused_params() - { - var latitude = "51.227741"; - var longitude = "6.773456"; + [Fact] + [Category("PayloadGenerator/Geolocation")] + public void geolocation_should_add_unused_params() + { + var latitude = "51.227741"; + var longitude = "6.773456"; - var generator = new PayloadGenerator.Geolocation(latitude, longitude); + var generator = new PayloadGenerator.Geolocation(latitude, longitude); - generator.ToString().ShouldBe("geo:51.227741,6.773456"); - } + generator.ToString().ShouldBe("geo:51.227741,6.773456"); + } - [Fact] - [Category("PayloadGenerator/PhoneNumber")] - public void phonenumber_should_build() - { - var number = "+495321123456"; + [Fact] + [Category("PayloadGenerator/PhoneNumber")] + public void phonenumber_should_build() + { + var number = "+495321123456"; - var generator = new PayloadGenerator.PhoneNumber(number); + var generator = new PayloadGenerator.PhoneNumber(number); - generator.ToString().ShouldBe("tel:+495321123456"); - } + generator.ToString().ShouldBe("tel:+495321123456"); + } - [Fact] - [Category("PayloadGenerator/Skype")] - public void skype_should_build() - { - var username = "johndoe123"; + [Fact] + [Category("PayloadGenerator/Skype")] + public void skype_should_build() + { + var username = "johndoe123"; - var generator = new PayloadGenerator.SkypeCall(username); + var generator = new PayloadGenerator.SkypeCall(username); - generator.ToString().ShouldBe("skype:johndoe123?call"); - } + generator.ToString().ShouldBe("skype:johndoe123?call"); + } - [Fact] - [Category("PayloadGenerator/Url")] - public void url_should_build_http() - { - var url = "http://code-bude.net"; + [Fact] + [Category("PayloadGenerator/Url")] + public void url_should_build_http() + { + var url = "http://code-bude.net"; - var generator = new PayloadGenerator.Url(url); + var generator = new PayloadGenerator.Url(url); - generator.ToString().ShouldBe("http://code-bude.net"); - } + generator.ToString().ShouldBe("http://code-bude.net"); + } - [Fact] - [Category("PayloadGenerator/Url")] - public void url_should_build_https() - { - var url = "https://code-bude.net"; + [Fact] + [Category("PayloadGenerator/Url")] + public void url_should_build_https() + { + var url = "https://code-bude.net"; - var generator = new PayloadGenerator.Url(url); + var generator = new PayloadGenerator.Url(url); - generator.ToString().ShouldBe("https://code-bude.net"); - } + generator.ToString().ShouldBe("https://code-bude.net"); + } - [Fact] - [Category("PayloadGenerator/Url")] - public void url_should_build_https_all_caps() - { - var url = "HTTPS://CODE-BUDE.NET"; + [Fact] + [Category("PayloadGenerator/Url")] + public void url_should_build_https_all_caps() + { + var url = "HTTPS://CODE-BUDE.NET"; - var generator = new PayloadGenerator.Url(url); + var generator = new PayloadGenerator.Url(url); - generator.ToString().ShouldBe("HTTPS://CODE-BUDE.NET"); - } + generator.ToString().ShouldBe("HTTPS://CODE-BUDE.NET"); + } - [Fact] - [Category("PayloadGenerator/Url")] - public void url_should_add_http() - { - var url = "code-bude.net"; + [Fact] + [Category("PayloadGenerator/Url")] + public void url_should_add_http() + { + var url = "code-bude.net"; - var generator = new PayloadGenerator.Url(url); + var generator = new PayloadGenerator.Url(url); - generator.ToString().ShouldBe("http://code-bude.net"); - } + generator.ToString().ShouldBe("http://code-bude.net"); + } - [Fact] - [Category("PayloadGenerator/Bookmark")] - public void bookmark_should_build() - { - var url = "http://code-bude.net"; - var title = "A nerd's blog"; + [Fact] + [Category("PayloadGenerator/Bookmark")] + public void bookmark_should_build() + { + var url = "http://code-bude.net"; + var title = "A nerd's blog"; - var generator = new PayloadGenerator.Bookmark(url, title); + var generator = new PayloadGenerator.Bookmark(url, title); - generator.ToString().ShouldBe("MEBKM:TITLE:A nerd's blog;URL:http\\://code-bude.net;;"); - } + generator.ToString().ShouldBe("MEBKM:TITLE:A nerd's blog;URL:http\\://code-bude.net;;"); + } - [Fact] - [Category("PayloadGenerator/Bookmark")] - public void bookmark_should_escape_input() - { - var url = "http://code-bude.net/fake,url.html"; - var title = "A nerd's blog: \\All;the;things\\"; + [Fact] + [Category("PayloadGenerator/Bookmark")] + public void bookmark_should_escape_input() + { + var url = "http://code-bude.net/fake,url.html"; + var title = "A nerd's blog: \\All;the;things\\"; - var generator = new PayloadGenerator.Bookmark(url, title); + var generator = new PayloadGenerator.Bookmark(url, title); - generator.ToString().ShouldBe("MEBKM:TITLE:A nerd's blog\\: \\\\All\\;the\\;things\\\\;URL:http\\://code-bude.net/fake\\,url.html;;"); - } + generator.ToString().ShouldBe("MEBKM:TITLE:A nerd's blog\\: \\\\All\\;the\\;things\\\\;URL:http\\://code-bude.net/fake\\,url.html;;"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_build_universal() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = false; - var begin = new DateTime(2016, 01, 03, 12, 00, 00); - var end = new DateTime(2016, 01, 03, 14, 30, 00); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_build_universal() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = false; + var begin = new DateTime(2016, 01, 03, 12, 00, 00); + var end = new DateTime(2016, 01, 03, 14, 30, 00); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_build_ical() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = false; - var begin = new DateTime(2016, 01, 03, 12, 00, 00); - var end = new DateTime(2016, 01, 03, 14, 30, 0); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.iCalComplete; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_build_ical() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = false; + var begin = new DateTime(2016, 01, 03, 12, 00, 00); + var end = new DateTime(2016, 01, 03, 14, 30, 0); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.iCalComplete; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VCALENDAR{Environment.NewLine}VERSION:2.0{Environment.NewLine}BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT{Environment.NewLine}END:VCALENDAR"); - } + generator.ToString().ShouldBe($"BEGIN:VCALENDAR{Environment.NewLine}VERSION:2.0{Environment.NewLine}BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT{Environment.NewLine}END:VCALENDAR"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_build_with_utc_datetime() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = false; - var begin = new DateTime(2016, 01, 03, 12, 00, 00, DateTimeKind.Utc); - var end = new DateTime(2016, 01, 03, 14, 30, 00, DateTimeKind.Utc); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_build_with_utc_datetime() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = false; + var begin = new DateTime(2016, 01, 03, 12, 00, 00, DateTimeKind.Utc); + var end = new DateTime(2016, 01, 03, 14, 30, 00, DateTimeKind.Utc); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000Z{Environment.NewLine}DTEND:20160103T143000Z{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000Z{Environment.NewLine}DTEND:20160103T143000Z{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_build_with_utc_offset() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = false; - var begin = new DateTimeOffset(2016, 01, 03, 12, 00, 00, new TimeSpan(3, 0, 0)); - var end = new DateTimeOffset(2016, 01, 03, 14, 30, 00, new TimeSpan(3, 0, 0)); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_build_with_utc_offset() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = false; + var begin = new DateTimeOffset(2016, 01, 03, 12, 00, 00, new TimeSpan(3, 0, 0)); + var end = new DateTimeOffset(2016, 01, 03, 14, 30, 00, new TimeSpan(3, 0, 0)); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T090000Z{Environment.NewLine}DTEND:20160103T113000Z{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T090000Z{Environment.NewLine}DTEND:20160103T113000Z{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_build_allday() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = true; - var begin = new DateTime(2016, 01, 03); - var end = new DateTime(2016, 01, 03); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_build_allday() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = true; + var begin = new DateTime(2016, 01, 03); + var end = new DateTime(2016, 01, 03); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103{Environment.NewLine}DTEND:20160103{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103{Environment.NewLine}DTEND:20160103{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_care_empty_fields() - { - var subject = "Release party"; - var description = ""; - var location = string.Empty; - var alldayEvent = false; - var begin = new DateTime(2016, 01, 03, 12, 00, 00); - var end = new DateTime(2016, 01, 03, 14, 30, 0); - var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_care_empty_fields() + { + var subject = "Release party"; + var description = ""; + var location = string.Empty; + var alldayEvent = false; + var begin = new DateTime(2016, 01, 03, 12, 00, 00); + var end = new DateTime(2016, 01, 03, 14, 30, 0); + var encoding = PayloadGenerator.CalendarEvent.EventEncoding.Universal; - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent, encoding); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/CalendarEvent")] - public void calendarevent_should_add_unused_params() - { - var subject = "Release party"; - var description = "A small party for the new QRCoder. Bring some beer!"; - var location = "Programmer's paradise, Beachtown, Paradise"; - var alldayEvent = false; - var begin = new DateTime(2016, 01, 03, 12, 00, 00); - var end = new DateTime(2016, 01, 03, 14, 30, 0); + [Fact] + [Category("PayloadGenerator/CalendarEvent")] + public void calendarevent_should_add_unused_params() + { + var subject = "Release party"; + var description = "A small party for the new QRCoder. Bring some beer!"; + var location = "Programmer's paradise, Beachtown, Paradise"; + var alldayEvent = false; + var begin = new DateTime(2016, 01, 03, 12, 00, 00); + var end = new DateTime(2016, 01, 03, 14, 30, 0); - var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent); + var generator = new PayloadGenerator.CalendarEvent(subject, description, location, begin, end, alldayEvent); - generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); - } + generator.ToString().ShouldBe($"BEGIN:VEVENT{Environment.NewLine}SUMMARY:Release party{Environment.NewLine}DESCRIPTION:A small party for the new QRCoder. Bring some beer!{Environment.NewLine}LOCATION:Programmer's paradise, Beachtown, Paradise{Environment.NewLine}DTSTART:20160103T120000{Environment.NewLine}DTEND:20160103T143000{Environment.NewLine}END:VEVENT"); + } - [Fact] - [Category("PayloadGenerator/IbanValidator")] - public void iban_validator_validate_german_iban() - { - var iban = "DE15268500010154131577"; + [Fact] + [Category("PayloadGenerator/IbanValidator")] + public void iban_validator_validate_german_iban() + { + var iban = "DE15268500010154131577"; - MethodInfo method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method.Invoke(null, new object[] { iban }); + var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); + var result = (bool)method.Invoke(null, new object[] { iban }); - result.ShouldBe(true); - } + result.ShouldBe(true); + } - [Fact] - [Category("PayloadGenerator/IbanValidator")] - public void iban_validator_validate_swiss_iban() - { - var iban = "CH1900767000U00121977"; + [Fact] + [Category("PayloadGenerator/IbanValidator")] + public void iban_validator_validate_swiss_iban() + { + var iban = "CH1900767000U00121977"; - MethodInfo method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method.Invoke(null, new object[] { iban }); + var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); + var result = (bool)method.Invoke(null, new object[] { iban }); - result.ShouldBe(true); - } + result.ShouldBe(true); + } - [Fact] - [Category("PayloadGenerator/IbanValidator")] - public void iban_validator_invalidates_iban() - { - var iban = "DE29268500010154131577"; + [Fact] + [Category("PayloadGenerator/IbanValidator")] + public void iban_validator_invalidates_iban() + { + var iban = "DE29268500010154131577"; - MethodInfo method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method.Invoke(null, new object[] { iban }); + var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); + var result = (bool)method.Invoke(null, new object[] { iban }); - result.ShouldBe(false); - } + result.ShouldBe(false); + } - [Fact] - [Category("PayloadGenerator/QrIbanValidator")] - public void qriban_validator_validates_iban() - { - var iban = "CH2430043000000789012"; + [Fact] + [Category("PayloadGenerator/QrIbanValidator")] + public void qriban_validator_validates_iban() + { + var iban = "CH2430043000000789012"; - MethodInfo method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method.Invoke(null, new object[] { iban }); + var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); + var result = (bool)method.Invoke(null, new object[] { iban }); - result.ShouldBe(true); - } + result.ShouldBe(true); + } - [Fact] - [Category("PayloadGenerator/QrIbanValidator")] - public void qriban_validator_invalidates_iban() - { - var iban = "CH3908704016075473007"; + [Fact] + [Category("PayloadGenerator/QrIbanValidator")] + public void qriban_validator_invalidates_iban() + { + var iban = "CH3908704016075473007"; - MethodInfo method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method.Invoke(null, new object[] { iban }); + var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); + var result = (bool)method.Invoke(null, new object[] { iban }); - result.ShouldBe(false); - } + result.ShouldBe(false); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_can_generate_payload_minimal() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_can_generate_payload_minimal() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount); + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount); - generator - .ToString() - .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n\n\n\n"); - } + generator + .ToString() + .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n\n\n\n"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_can_generate_payload_full() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; - - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser, - PayloadGenerator.Girocode.GirocodeVersion.Version1, - PayloadGenerator.Girocode.GirocodeEncoding.ISO_8859_1); - - generator - .ToString() - .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); - } + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_can_generate_payload_full() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; + + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser, + PayloadGenerator.Girocode.GirocodeVersion.Version1, + PayloadGenerator.Girocode.GirocodeEncoding.ISO_8859_1); + + generator + .ToString() + .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_handle_version() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; - - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser, - PayloadGenerator.Girocode.GirocodeVersion.Version2, - PayloadGenerator.Girocode.GirocodeEncoding.ISO_8859_1); - - generator - .ToString() - .ShouldBe("BCD\n002\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); - } + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_handle_version() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; + + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser, + PayloadGenerator.Girocode.GirocodeVersion.Version2, + PayloadGenerator.Girocode.GirocodeEncoding.ISO_8859_1); + + generator + .ToString() + .ShouldBe("BCD\n002\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_handle_iban_whitespaces() - { - var iban = "DE33 1002 0500 0001 1947 00"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; - - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); - - generator - .ToString() - .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); - } + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_handle_iban_whitespaces() + { + var iban = "DE33 1002 0500 0001 1947 00"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; + + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); + + generator + .ToString() + .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_handle_bic_whitespaces() - { - var iban = "DE33100205000001194700"; - var bic = "BFSW DE 33 BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; - - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); - - generator - .ToString() - .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); - } + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_handle_bic_whitespaces() + { + var iban = "DE33100205000001194700"; + var bic = "BFSW DE 33 BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; + + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); + + generator + .ToString() + .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_fill_amount_decimals() - { - var iban = "DE33100205000001194700"; - var bic = "BFSW DE 33 BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 12m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; - - var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); - - generator - .ToString() - .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR12.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); - } + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_fill_amount_decimals() + { + var iban = "DE33100205000001194700"; + var bic = "BFSW DE 33 BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 12m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; + + var generator = new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser); + + generator + .ToString() + .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR12.00\n1234\n\nDonation to Wikipedia.\nThanks for using Girocode"); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_iban_exception() - { - var iban = "33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_iban_exception() + { + var iban = "33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The IBAN entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The IBAN entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_bic_exception() - { - var iban = "DE33100205000001194700"; - var bic = "DWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_bic_exception() + { + var iban = "DE33100205000001194700"; + var bic = "DWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The BIC entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The BIC entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_name_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "A company with a name which is exactly 71 chars - and for that to long."; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_name_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "A company with a name which is exactly 71 chars - and for that to long."; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_amount_decimals_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.521m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_amount_decimals_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.521m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_amount_min_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 0.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_amount_min_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 0.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_amount_max_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 1999999999.99m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_amount_max_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 1999999999.99m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_purpose_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "12345"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_purpose_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "12345"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Purpose of credit transfer can only have 4 chars at maximum."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Purpose of credit transfer can only have 4 chars at maximum."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_remittance_unstructured_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "An unstructured remittance information which is longer than a tweet. This means that this unstructures remittance info has more than 140 chars."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_remittance_unstructured_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "An unstructured remittance information which is longer than a tweet. This means that this unstructures remittance info has more than 140 chars."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Unstructured reference texts have to be shorter than 141 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Unstructured reference texts have to be shorter than 141 chars."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_remittance_structured_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Structured remittance infos have to be shorter than 36 chars."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "Thanks for using Girocode"; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_remittance_structured_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Structured remittance infos have to be shorter than 36 chars."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "Thanks for using Girocode"; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Structured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Structured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Structured reference texts have to be shorter than 36 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Structured reference texts have to be shorter than 36 chars."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_should_throw_usermessage_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; - var remittanceInformation = "Donation to Wikipedia."; - var purposeOfCreditTransfer = "1234"; - var messageToGirocodeUser = "The usermessage is shown to the user which scans the Girocode. It has to be shorter than 71 chars."; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_should_throw_usermessage_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; + var remittanceInformation = "Donation to Wikipedia."; + var purposeOfCreditTransfer = "1234"; + var messageToGirocodeUser = "The usermessage is shown to the user which scans the Girocode. It has to be shorter than 71 chars."; - var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, - PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); + var exception = Record.Exception(() => new PayloadGenerator.Girocode(iban, bic, name, amount, remittanceInformation, + PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, purposeOfCreditTransfer, messageToGirocodeUser)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Message to the Girocode-User reader texts have to be shorter than 71 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Message to the Girocode-User reader texts have to be shorter than 71 chars."); + } - [Fact] - [Category("PayloadGenerator/Girocode")] - public void girocode_generator_sets_encoding_parameters() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/Girocode")] + public void girocode_generator_sets_encoding_parameters() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var payload = new PayloadGenerator.Girocode(iban, bic, name, amount); + var payload = new PayloadGenerator.Girocode(iban, bic, name, amount); - payload.EccLevel.ShouldBe(ECCLevel.M); - payload.EciMode.ShouldBe(EciMode.Default); - payload.Version.ShouldBe(-1); - } + payload.EccLevel.ShouldBe(ECCLevel.M); + payload.EciMode.ShouldBe(EciMode.Default); + payload.Version.ShouldBe(-1); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singlepayment_minimal() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singlepayment_minimal() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singlepayment_full() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var amount = 10.00m; - var postingKey = 69; - Currency currency = Currency.USD; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singlepayment_full() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var amount = 10.00m; + var postingKey = 69; + var currency = Currency.USD; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singledirectdebit() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var amount = 10.00m; - var postingKey = 69; - Currency currency = Currency.USD; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singledirectdebit() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var amount = 10.00m; + var postingKey = 69; + var currency = Currency.USD; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebit, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebit, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, DateTime.Now); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singledirectdebit?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singledirectdebit?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_periodicsinglepayment() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var amount = 10.00m; - var postingKey = 69; - var periodicTimeunit = "W"; - var periodicTimeunitRotation = 2; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_periodicsinglepayment() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var amount = 10.00m; + var postingKey = 69; + var periodicTimeunit = "W"; + var periodicTimeunitRotation = 2; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://periodicsinglepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=W&periodictimeunitrotation=2&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); - } + generator + .ToString() + .ShouldBe("bank://periodicsinglepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&postingkey=69&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=W&periodictimeunitrotation=2&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_minimal() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_minimal() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); - generator - .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_full() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - Currency currency = Currency.USD; - - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban, bic, amount, "", 0, null, null, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); - - generator - .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singlepaymentsepa_full() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var currency = Currency.USD; + + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban, bic, amount, "", 0, null, null, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); + + generator + .ToString() + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_singledirectdebitsepa() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var creditorId = "DE 02 TSV 01234567890"; - var mandateId = "987543CB2"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - Currency currency = Currency.USD; - - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); - - generator - .ToString() - .ShouldBe("bank://singledirectdebitsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&creditorid=DE%2002%20TSV%2001234567890&mandateid=987543CB2&dateofsignature=01032017&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_singledirectdebitsepa() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var creditorId = "DE 02 TSV 01234567890"; + var mandateId = "987543CB2"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var currency = Currency.USD; + + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); + + generator + .ToString() + .ShouldBe("bank://singledirectdebitsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&creditorid=DE%2002%20TSV%2001234567890&mandateid=987543CB2&dateofsignature=01032017&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_periodicsinglepaymentsepa() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - var periodicTimeunit = "M"; - var periodicTimeunitRotation = 1; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; - - var generator = new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); - - generator - .ToString() - .ShouldBe("bank://periodicsinglepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=M&periodictimeunitrotation=1&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_periodicsinglepaymentsepa() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var periodicTimeunit = "M"; + var periodicTimeunitRotation = 1; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; + + var generator = new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now); + + generator + .ToString() + .ShouldBe("bank://periodicsinglepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&separeference=Fake%20SEPA%20reference&amount=10,00&reason=Thanks%20for%20all%20your%20efforts¤cy=USD&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + "&periodictimeunit=M&periodictimeunitrotation=1&periodicfirstexecutiondate=" + periodicFirstExecutionDate.ToString("ddMMyyyy") + "&periodiclastexecutiondate=" + periodicLastExecutionDate.ToString("ddMMyyyy")); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_contact() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_contact() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact, name, account: account, bnc: bnc); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact, name, account: account, bnc: bnc); - generator - .ToString() - .ShouldBe("bank://contact?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000"); - } + generator + .ToString() + .ShouldBe("bank://contact?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000"); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_contact_full() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_contact_full() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact, name, account, bnc, "", "", "New business contact."); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact, name, account, bnc, "", "", "New business contact."); - generator - .ToString() - .ShouldBe("bank://contact?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&reason=New%20business%20contact."); - } + generator + .ToString() + .ShouldBe("bank://contact?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&reason=New%20business%20contact."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_contactv2_classic() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_contactv2_classic() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, account: account, bnc: bnc); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, account: account, bnc: bnc); - generator - .ToString() - .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000"); - } + generator + .ToString() + .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000"); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_contactv2_sepa() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_contactv2_sepa() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, iban: iban, bic: bic); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, iban: iban, bic: bic); - generator - .ToString() - .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER"); - } + generator + .ToString() + .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER"); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_can_generate_payload_contactv2_sepa_full() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_can_generate_payload_contactv2_sepa_full() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, "", "", iban, bic, "A new v2 contact."); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.contact_v2, name, "", "", iban, bic, "A new v2 contact."); - generator - .ToString() - .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&reason=A%20new%20v2%20contact."); - } + generator + .ToString() + .ShouldBe("bank://contact_v2?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&reason=A%20new%20v2%20contact."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_handle_account_whitespaces() - { - var account = "01 194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_handle_account_whitespaces() + { + var account = "01 194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=01194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=01194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_handle_bnc_whitespaces() - { - var account = "001194700"; - var bnc = "10020 5000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_handle_bnc_whitespaces() + { + var account = "001194700"; + var bnc = "10020 5000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_handle_iban_whitespaces() - { - var iban = "DE33 100205000 0011947 00"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_handle_iban_whitespaces() + { + var iban = "DE33 100205000 0011947 00"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); - generator - .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_handle_bic_whitespaces() - { - var iban = "DE33100205000001194700"; - var bic = "BF SWDE3 3BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_handle_bic_whitespaces() + { + var iban = "DE33100205000001194700"; + var bic = "BF SWDE3 3BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount); - generator - .ToString() - .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepaymentsepa?name=Wikimedia%20F%C3%B6rdergesellschaft&iban=DE33100205000001194700&bic=BFSWDE33BER&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_add_decimals() - { - var account = "001194700"; - var bnc = "10020 5000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_add_decimals() + { + var account = "001194700"; + var bnc = "10020 5000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10; #pragma warning disable CS0612 - var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); + var generator = new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount); #pragma warning restore CS0612 - generator - .ToString() - .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); - } + generator + .ToString() + .ShouldBe("bank://singlepayment?name=Wikimedia%20F%C3%B6rdergesellschaft&account=001194700&bnc=100205000&amount=10,00¤cy=EUR&executiondate=" + DateTime.Now.ToString("ddMMyyyy") + ""); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_contact_constructor_exception() - { - var account = "0001194700"; - var bnc = "10020 5000"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_contact_constructor_exception() + { + var account = "0001194700"; + var bnc = "10020 5000"; + var name = "Wikimedia Fördergesellschaft"; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, "", "", "New business contact.")); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_contact_v2_constructor_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_contact_v2_constructor_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban: iban, bic: bic)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban: iban, bic: bic)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The constructor without an amount may only ne used with authority types 'contact' and 'contact_v2'."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_exception() - { - var account = "0001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_exception() + { + var account = "0001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, account: account, bnc: bnc, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, account: account, bnc: bnc, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The constructor with 'account' and 'bnc' may only be used with 'non SEPA' authority types. Either choose another authority type or switch constructor."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_periodic_exception() - { - var account = "0001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var amount = 10.00m; - var postingKey = 69; - var periodicTimeunit = ""; - var periodicTimeunitRotation = 2; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_nonsepa_constructor_periodic_exception() + { + var account = "0001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var amount = 10.00m; + var postingKey = 69; + var periodicTimeunit = ""; + var periodicTimeunitRotation = 2; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepayment, name, account, bnc, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, reason, postingKey, currency, DateTime.Now)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("When using 'periodicsinglepayment' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_sepa_constructor_exception() - { - var iban = "DE33 100205000 0011947 00"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_sepa_constructor_exception() + { + var iban = "DE33 100205000 0011947 00"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, iban: iban, bic: bic, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The constructor with 'iban' and 'bic' may only be used with 'SEPA' authority types. Either choose another authority type or switch constructor."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_wrong_sepa_constructor_periodic_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - var periodicTimeunit = "M"; - var periodicTimeunitRotation = 0; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_wrong_sepa_constructor_periodic_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var periodicTimeunit = "M"; + var periodicTimeunitRotation = 0; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("When using 'periodicsinglepaymentsepa' as authority type, the parameters 'periodicTimeunit' and 'periodicTimeunitRotation' must be set."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_name_too_long_exception() - { - var iban = "DE33 100205000 0011947 00"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft has really really really long name, over 71 chars"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_name_too_long_exception() + { + var iban = "DE33 100205000 0011947 00"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft has really really really long name, over 71 chars"; + var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("(Payee-)Name must be shorter than 71 chars."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_reason_too_long_exception() - { - var iban = "DE33 100205000 0011947 00"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "A long long long reason text which may resolve in an exception"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_reason_too_long_exception() + { + var iban = "DE33 100205000 0011947 00"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "A long long long reason text which may resolve in an exception"; + var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount, reason: reason)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount, reason: reason)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Reasons texts have to be shorter than 28 chars."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Reasons texts have to be shorter than 28 chars."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_account_exception() - { - var account = "1194700AD"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_account_exception() + { + var account = "1194700AD"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The account entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The account entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_bnc_exception() - { - var account = "001194700"; - var bnc = "10020500023545626226262"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_bnc_exception() + { + var account = "001194700"; + var bnc = "10020500023545626226262"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The bnc entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The bnc entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_postingkey_exception() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var postingKey = 101; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_postingkey_exception() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var postingKey = 101; + var amount = 10.00m; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account: account, bnc: bnc, amount: amount, postingKey: postingKey)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("PostingKey must be within 0 and 99."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("PostingKey must be within 0 and 99."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_iban_exception() - { - var iban = "DE33100205AZB000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_iban_exception() + { + var iban = "DE33100205AZB000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The IBAN entered isn't valid."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The IBAN entered isn't valid."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_bic_exception() - { - var iban = "DE33100205000001194700"; - var bic = "B2FSWDE33BER99871ABC99998"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.00m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_bic_exception() + { + var iban = "DE33100205000001194700"; + var bic = "B2FSWDE33BER99871ABC99998"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.00m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The BIC entered isn't valid."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The BIC entered isn't valid."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_separeference_too_long_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var creditorId = "DE 02 TSV 01234567890"; - var mandateId = "987543CB2"; - var sepaReference = "Fake SEPA reference which is also much to long for the reference field."; - var amount = 10.00m; - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("SEPA reference texts have to be shorter than 36 chars."); + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_separeference_too_long_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var creditorId = "DE 02 TSV 01234567890"; + var mandateId = "987543CB2"; + var sepaReference = "Fake SEPA reference which is also much to long for the reference field."; + var amount = 10.00m; + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("SEPA reference texts have to be shorter than 36 chars."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_creditorid_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var creditorId = "12DE 02 TSV 01234567890"; - var mandateId = "987543CB2"; - var sepaReference = "Fake SEPA reference."; - var amount = 10.00m; - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The creditorId entered isn't valid."); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_creditorid_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var creditorId = "12DE 02 TSV 01234567890"; + var mandateId = "987543CB2"; + var sepaReference = "Fake SEPA reference."; + var amount = 10.00m; + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The creditorId entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_mandateid_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var creditorId = "DE 02 TSV 01234567890"; - var mandateId = "ÄÖ987543CB2 1990 2017"; - var sepaReference = "Fake SEPA reference."; - var amount = 10.00m; - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The mandateId entered isn't valid."); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_mandateid_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var creditorId = "DE 02 TSV 01234567890"; + var mandateId = "ÄÖ987543CB2 1990 2017"; + var sepaReference = "Fake SEPA reference."; + var amount = 10.00m; + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singledirectdebitsepa, name, iban, bic, amount, "", 0, null, null, creditorId, mandateId, new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The mandateId entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_amount_too_much_digits_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 10.001m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_amount_too_much_digits_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 10.001m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount must have less than 3 digits after decimal point."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_amount_too_big_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var amount = 1000000000m; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_amount_too_big_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var amount = 1000000000m; - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepaymentsepa, name, iban: iban, bic: bic, amount: amount)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount has to at least 0.01 and must be smaller or equal to 999999999.99."); - } + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_executiondate_exception() - { - var account = "001194700"; - var bnc = "100205000"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var amount = 10.00m; - var postingKey = 69; - var executionDate = new DateTime(2017, 1, 1); - Currency currency = Currency.USD; + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_executiondate_exception() + { + var account = "001194700"; + var bnc = "100205000"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var amount = 10.00m; + var postingKey = 69; + var executionDate = new DateTime(2017, 1, 1); + var currency = Currency.USD; #pragma warning disable CS0612 - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.singlepayment, name, account, bnc, amount, "", 0, null, null, reason, postingKey, currency, executionDate)); #pragma warning restore CS0612 - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Execution date must be today or in future."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Execution date must be today or in future."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_periodictimeunit_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - var periodicTimeunit = "Z"; - var periodicTimeunitRotation = 1; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_periodictimeunit_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var periodicTimeunit = "Z"; + var periodicTimeunitRotation = 1; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The periodicTimeunit must be either 'M' (monthly) or 'W' (weekly)."); + } - [Fact] - [Category("PayloadGenerator/BezahlCode")] - public void bezahlcode_generator_should_throw_invalid_periodictimeunitrotation_exception() - { - var iban = "DE33100205000001194700"; - var bic = "BFSWDE33BER"; - var name = "Wikimedia Fördergesellschaft"; - var reason = "Thanks for all your efforts"; - var sepaReference = "Fake SEPA reference"; - var amount = 10.00m; - var periodicTimeunit = "M"; - var periodicTimeunitRotation = 128; - var periodicFirstExecutionDate = DateTime.Now; - var periodicLastExecutionDate = DateTime.Now.AddMonths(3); - Currency currency = Currency.USD; - - var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); - } + [Fact] + [Category("PayloadGenerator/BezahlCode")] + public void bezahlcode_generator_should_throw_invalid_periodictimeunitrotation_exception() + { + var iban = "DE33100205000001194700"; + var bic = "BFSWDE33BER"; + var name = "Wikimedia Fördergesellschaft"; + var reason = "Thanks for all your efforts"; + var sepaReference = "Fake SEPA reference"; + var amount = 10.00m; + var periodicTimeunit = "M"; + var periodicTimeunitRotation = 128; + var periodicFirstExecutionDate = DateTime.Now; + var periodicLastExecutionDate = DateTime.Now.AddMonths(3); + var currency = Currency.USD; + + var exception = Record.Exception(() => new PayloadGenerator.BezahlCode(AuthorityType.periodicsinglepaymentsepa, name, iban, bic, amount, periodicTimeunit, periodicTimeunitRotation, periodicFirstExecutionDate, periodicLastExecutionDate, "", "", new DateTime(2017, 03, 01), reason, sepaReference, currency, DateTime.Now)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The periodicTimeunitRotation must be 1 or greater. (It means repeat the payment every 'periodicTimeunitRotation' weeks/months."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_reference_not_allowed() - { - var refType = ReferenceType.NON; - var reference = "1234567890123456"; - var refTextType = ReferenceTextType.CreditorReferenceIso11649; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_reference_not_allowed() + { + var refType = ReferenceType.NON; + var reference = "1234567890123456"; + var refTextType = ReferenceTextType.CreditorReferenceIso11649; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Reference is only allowed when referenceType not equals \"NON\""); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Reference is only allowed when referenceType not equals \"NON\""); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_missing_reftexttype() - { - var refType = ReferenceType.SCOR; - var reference = "1234567890123456"; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_missing_reftexttype() + { + var refType = ReferenceType.SCOR; + var reference = "1234567890123456"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("You have to set an ReferenceTextType when using the reference text."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("You have to set an ReferenceTextType when using the reference text."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_qrr_ref_too_long() - { - var refType = ReferenceType.QRR; - var reference = "9900050000000003200710123031234654574398214093682164062138462089364"; - var refTextType = ReferenceTextType.QrReference; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_qrr_ref_too_long() + { + var refType = ReferenceType.QRR; + var reference = "9900050000000003200710123031234654574398214093682164062138462089364"; + var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("QR-references have to be shorter than 28 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("QR-references have to be shorter than 28 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_qrr_ref_wrong_char() - { - var refType = ReferenceType.QRR; - var reference = "99000ABCDF5000032007101230"; - var refTextType = ReferenceTextType.QrReference; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_qrr_ref_wrong_char() + { + var refType = ReferenceType.QRR; + var reference = "99000ABCDF5000032007101230"; + var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("QR-reference must exist out of digits only."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("QR-reference must exist out of digits only."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_qrr_ref_checksum_invalid() - { - var refType = ReferenceType.QRR; - var reference = "990005000000000320071012304"; - var refTextType = ReferenceTextType.QrReference; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_qrr_ref_checksum_invalid() + { + var refType = ReferenceType.QRR; + var reference = "990005000000000320071012304"; + var refTextType = ReferenceTextType.QrReference; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("QR-references is invalid. Checksum error."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("QR-references is invalid. Checksum error."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Reference")] - public void swissqrcode_generator_should_throw_iso11649_ref_too_long() - { - var refType = ReferenceType.QRR; - var reference = "99000500000000032007101230312346545743982162138462089364"; - var refTextType = ReferenceTextType.CreditorReferenceIso11649; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Reference")] + public void swissqrcode_generator_should_throw_iso11649_ref_too_long() + { + var refType = ReferenceType.QRR; + var reference = "99000500000000032007101230312346545743982162138462089364"; + var refTextType = ReferenceTextType.CreditorReferenceIso11649; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Reference(refType, reference, refTextType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Creditor references (ISO 11649) have to be shorter than 26 chars."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Creditor references (ISO 11649) have to be shorter than 26 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.AdditionalInformation")] - public void swissqrcode_generator_should_throw_unstructured_msg_too_long() - { - var billInformation = "This is sample bill information with a length below 140."; - var unstructuredMessage = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum"; + [Fact] + [Category("PayloadGenerator/SwissQrCode.AdditionalInformation")] + public void swissqrcode_generator_should_throw_unstructured_msg_too_long() + { + var billInformation = "This is sample bill information with a length below 140."; + var unstructuredMessage = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.AdditionalInformation(unstructuredMessage, billInformation)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.AdditionalInformation(unstructuredMessage, billInformation)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Unstructured message and bill information must be shorter than 141 chars in total/combined."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Unstructured message and bill information must be shorter than 141 chars in total/combined."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_generate_iban() - { - var iban = "CH2609000000857666015"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_generate_iban() + { + var iban = "CH2609000000857666015"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); + var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); - generator - .ToString() - .ShouldBe("CH2609000000857666015"); - } + generator + .ToString() + .ShouldBe("CH2609000000857666015"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_generate_iban_2() - { - var iban = "CH47048350000GABRIELS"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_generate_iban_2() + { + var iban = "CH47048350000GABRIELS"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); + var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); - generator - .ToString() - .ShouldBe("CH47048350000GABRIELS"); - } + generator + .ToString() + .ShouldBe("CH47048350000GABRIELS"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_generate_iban_qr() - { - var iban = "CH2430043000000789012"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_generate_iban_qr() + { + var iban = "CH2430043000000789012"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban; - var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); + var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); - generator - .ToString() - .ShouldBe("CH2430043000000789012"); - } + generator + .ToString() + .ShouldBe("CH2430043000000789012"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_remove_spaces_iban() - { - var iban = "CH26 0900 0000 8576 6601 5"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_remove_spaces_iban() + { + var iban = "CH26 0900 0000 8576 6601 5"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); + var generator = new PayloadGenerator.SwissQrCode.Iban(iban, ibanType); - generator - .ToString() - .ShouldBe("CH2609000000857666015"); - } + generator + .ToString() + .ShouldBe("CH2609000000857666015"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_throw_invalid_iban() - { - var iban = "CHC2609000000857666015"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_throw_invalid_iban() + { + var iban = "CHC2609000000857666015"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The IBAN entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The IBAN entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_throw_invalid_qriban() - { - var iban = "CHC2609000000857666015"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_throw_invalid_qriban() + { + var iban = "CHC2609000000857666015"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The QR-IBAN entered isn't valid."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The QR-IBAN entered isn't valid."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Iban")] - public void swissqrcode_generator_should_throw_ivalid_iban_country() - { - var iban = "DE2609000000857666015"; - var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Iban")] + public void swissqrcode_generator_should_throw_ivalid_iban_country() + { + var iban = "DE2609000000857666015"; + var ibanType = PayloadGenerator.SwissQrCode.Iban.IbanType.Iban; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode.Iban(iban, ibanType)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The IBAN must start with \"CH\" or \"LI\"."); - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The IBAN must start with \"CH\" or \"LI\"."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_generate_contact_simple() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_generate_contact_simple() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; - var generator = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, null, null); + var generator = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, null, null); - generator - .ToString() - .ShouldBe("S\r\nJohn Doe\r\n\r\n\r\n3003\r\nBern\r\nCH\r\n"); - } + generator + .ToString() + .ShouldBe("S\r\nJohn Doe\r\n\r\n\r\n3003\r\nBern\r\nCH\r\n"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_generate_contact_full() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var generator = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber); - - generator - .ToString() - .ShouldBe("S\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_generate_contact_full() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var generator = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber); + + generator + .ToString() + .ShouldBe("S\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_name_empty() - { - var name = ""; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Name must not be empty."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_name_empty() + { + var name = ""; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Name must not be empty."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_name_too_long() - { - var name = "John Dorian Peter Charles Lord of the Rings and Master of Disaster Grayham"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Name must be shorter than 71 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_name_too_long() + { + var name = "John Dorian Peter Charles Lord of the Rings and Master of Disaster Grayham"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Name must be shorter than 71 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_street_too_long() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude in der wunderschönen aber auch ziemlich teuren Stadt Bern in der Schweiz"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Street must be shorter than 71 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_street_too_long() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude in der wunderschönen aber auch ziemlich teuren Stadt Bern in der Schweiz"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Street must be shorter than 71 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_street_with_illegal_char() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude 1 ♥"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe(@"Street must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_street_with_illegal_char() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude 1 ♥"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe(@"Street must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_housenumber_too_long() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "123456789123456789"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("House number must be shorter than 17 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_housenumber_too_long() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "123456789123456789"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("House number must be shorter than 17 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_zip_empty() - { - var name = "John Doe"; - var zip = ""; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Zip code must not be empty."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_zip_empty() + { + var name = "John Doe"; + var zip = ""; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Zip code must not be empty."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_zip_too_long() - { - var name = "John Doe"; - var zip = "30031234567891234"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Zip code must be shorter than 17 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_zip_too_long() + { + var name = "John Doe"; + var zip = "30031234567891234"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Zip code must be shorter than 17 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_zip_has_illegal_char() - { - var name = "John Doe"; - var zip = "3003CHF♥"; - var city = "Bern"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe(@"Zip code must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_zip_has_illegal_char() + { + var name = "John Doe"; + var zip = "3003CHF♥"; + var city = "Bern"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe(@"Zip code must match the following pattern as defined in pain.001: ^([a-zA-Z0-9\.,;:'\ \+\-/\(\)?\*\[\]\{\}\\`´~ ^|]|[!""#%&<>÷=@_$£¡¢¤¥¦§¨©ª«¬®¯°±²³µ¶·¸¹º»¼½¾¿×Ø€]|[àáâäãåāăąçćĉċčďđèéêëēĕėęěĝğġģĥħìíîïĩīĭįıijķĸĺļľŀłñńņňʼnŋòóôöōŏőõŕŗřśŝşšșţťŧțùúûüũūŭůűųŵýÿŷźżžßÀÁÂÄÃÅĀĂĄÇĆĈĊČĎĐÈÉÊËĒĔĖĘĚĜĞĠĢĤĦÌÍÎÏĨĪĬĮİIJĴĵĶĹĻĽĿŁÑŃŅŇŊÒÓÔÖÕŌŎŐŔŖŘŚŜŞŠȘŢŤŦȚÙÚÛÜŨŪŬŮŰŲŴÝŶŸŹŻŽÆÐÞæðøþŒœſ])*$"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_city_empty() - { - var name = "John Doe"; - var zip = "3003"; - var city = ""; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("City must not be empty."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_city_empty() + { + var name = "John Doe"; + var zip = "3003"; + var city = ""; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("City must not be empty."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_city_too_long() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Berner-Sangerhausen-Ober-Hinter-der-Alm-Stadt-am-Unter-Über-Berg"; - var country = "CH"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("City name must be shorter than 36 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_city_too_long() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Berner-Sangerhausen-Ober-Hinter-der-Alm-Stadt-am-Unter-Über-Berg"; + var country = "CH"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("City name must be shorter than 36 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode.Contact")] - public void swissqrcode_generator_should_throw_wrong_countrycode() - { - var name = "John Doe"; - var zip = "3003"; - var city = "Bern"; - var country = "CHE"; - var street = "Parlamentsgebäude"; - var houseNumber = "1"; - - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode.Contact")] + public void swissqrcode_generator_should_throw_wrong_countrycode() + { + var name = "John Doe"; + var zip = "3003"; + var city = "Bern"; + var country = "CHE"; + var street = "Parlamentsgebäude"; + var houseNumber = "1"; + + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country, street, houseNumber)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_generate_swisscode_simple() - { - var creditor = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var currency = PayloadGenerator.SwissQrCode.Currency.EUR; + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_generate_swisscode_simple() + { + var creditor = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var currency = PayloadGenerator.SwissQrCode.Currency.EUR; - var generator = new PayloadGenerator.SwissQrCode(iban, currency, creditor, reference); + var generator = new PayloadGenerator.SwissQrCode(iban, currency, creditor, reference); - generator - .ToString() - .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nQRR\r\n990005000000000320071012303\r\n\r\nEPD"); - } + generator + .ToString() + .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nQRR\r\n990005000000000320071012303\r\n\r\nEPD"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_generate_swisscode_full() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral); - - generator - .ToString() - .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here..."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_generate_swisscode_full() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral); + + generator + .ToString() + .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here..."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_sets_encoding_parameters() - { - var creditor = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var currency = PayloadGenerator.SwissQrCode.Currency.EUR; + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_sets_encoding_parameters() + { + var creditor = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var currency = PayloadGenerator.SwissQrCode.Currency.EUR; - var payload = new PayloadGenerator.SwissQrCode(iban, currency, creditor, reference); + var payload = new PayloadGenerator.SwissQrCode(iban, currency, creditor, reference); - payload.EccLevel.ShouldBe(ECCLevel.M); - payload.EciMode.ShouldBe(EciMode.Utf8); - payload.Version.ShouldBe(-1); - } + payload.EccLevel.ShouldBe(ECCLevel.M); + payload.EciMode.ShouldBe(EciMode.Utf8); + payload.Version.ShouldBe(-1); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_generate_clean_end_linebreaks() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message."); - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral); - - generator - .ToString() - .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_generate_clean_end_linebreaks() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message."); + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral); + + generator + .ToString() + .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_generate_swisscode_full_alt() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, "alt1", "alt2"); - - generator - .ToString() - .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here...\r\nalt1\r\nalt2"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_generate_swisscode_full_alt() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, "alt1", "alt2"); + + generator + .ToString() + .ShouldBe("SPC\r\n0200\r\n1\r\nCH2430043000000789012\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n100.25\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nQRR\r\n990005000000000320071012303\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here...\r\nalt1\r\nalt2"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_not_generate_space_as_thousands_separator() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2609000000857666015", PayloadGenerator.SwissQrCode.Iban.IbanType.Iban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.SCOR, "99000500000000032003", ReferenceTextType.CreditorReferenceIso11649); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var amount = 1234567.89m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, "alt1", "alt2"); - - generator - .ToString() - .ShouldBe("SPC\r\n0200\r\n1\r\nCH2609000000857666015\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n1234567.89\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nSCOR\r\n99000500000000032003\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here...\r\nalt1\r\nalt2"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_not_generate_space_as_thousands_separator() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2609000000857666015", PayloadGenerator.SwissQrCode.Iban.IbanType.Iban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.SCOR, "99000500000000032003", ReferenceTextType.CreditorReferenceIso11649); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var amount = 1234567.89m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var generator = new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, "alt1", "alt2"); + + generator + .ToString() + .ShouldBe("SPC\r\n0200\r\n1\r\nCH2609000000857666015\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n1234567.89\r\nCHF\r\nS\r\nJohn Doe\r\nParlamentsgebäude\r\n1\r\n3003\r\nBern\r\nCH\r\nSCOR\r\n99000500000000032003\r\nThis is my unstructured message.\r\nEPD\r\nSome bill information here...\r\nalt1\r\nalt2"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_throw_amount_too_big() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2609000000857666015", PayloadGenerator.SwissQrCode.Iban.IbanType.Iban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var amount = 1234567891.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Amount (including decimals) must be shorter than 13 places."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_throw_amount_too_big() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2609000000857666015", PayloadGenerator.SwissQrCode.Iban.IbanType.Iban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR, "990005000000000320071012303", ReferenceTextType.QrReference); + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var amount = 1234567891.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Amount (including decimals) must be shorter than 13 places."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_throw_incompatible_reftype() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.NON); - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_throw_incompatible_reftype() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.NON); + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("If QR-IBAN is used, you have to choose \"QRR\" as reference type!"); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_throw_alt1_too_long() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR); - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - var alt1 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; - - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Alternative procedure information block 1 must be shorter than 101 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_throw_alt1_too_long() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR); + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + var alt1 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; + + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Alternative procedure information block 1 must be shorter than 101 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_throw_alt2_too_long() - { - var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); - var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); - var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR); - var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); - var currency = PayloadGenerator.SwissQrCode.Currency.CHF; - var amount = 100.25m; - var reqDateOfPayment = new DateTime(2017, 03, 01); - var alt1 = "lorem ipsum"; - var alt2 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; - var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1, alt2)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Alternative procedure information block 2 must be shorter than 101 chars."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_throw_alt2_too_long() + { + var contactGeneral = PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress("John Doe", "3003", "Bern", "CH", "Parlamentsgebäude", "1"); + var iban = new PayloadGenerator.SwissQrCode.Iban("CH2430043000000789012", PayloadGenerator.SwissQrCode.Iban.IbanType.QrIban); + var reference = new PayloadGenerator.SwissQrCode.Reference(ReferenceType.QRR); + var additionalInformation = new PayloadGenerator.SwissQrCode.AdditionalInformation("This is my unstructured message.", "Some bill information here..."); + var currency = PayloadGenerator.SwissQrCode.Currency.CHF; + var amount = 100.25m; + var reqDateOfPayment = new DateTime(2017, 03, 01); + var alt1 = "lorem ipsum"; + var alt2 = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean ma"; + var exception = Record.Exception(() => new PayloadGenerator.SwissQrCode(iban, currency, contactGeneral, reference, additionalInformation, contactGeneral, amount, reqDateOfPayment, contactGeneral, alt1, alt2)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Alternative procedure information block 2 must be shorter than 101 chars."); + } - [Fact] - [Category("PayloadGenerator/SwissQrCode")] - public void swissqrcode_generator_should_validate_two_lettercodes() - { - string name = "John Doe"; - string zip = "12345"; - string city = "Gotham City"; - - // Should work, as DE is a valid country code - string country = "DE"; - var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); - - // Should work, as de is a valid country code and case should be ignored - country = "de"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); - - // Should work, as XK is is defined as special case (not officially ISO-3166-1,but used in the wild) - // See https://en.wikipedia.org/wiki/XK_(user_assigned_code) and https://github.com/codebude/QRCoder/issues/420 - country = "XK"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - Assert.Null(exception); - - - // Should throw exception, as ZZ isn't a valid country code - country = "ZZ"; - exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); - - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); - } + [Fact] + [Category("PayloadGenerator/SwissQrCode")] + public void swissqrcode_generator_should_validate_two_lettercodes() + { + string name = "John Doe"; + string zip = "12345"; + string city = "Gotham City"; + + // Should work, as DE is a valid country code + string country = "DE"; + var exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); + Assert.Null(exception); + + // Should work, as de is a valid country code and case should be ignored + country = "de"; + exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); + Assert.Null(exception); + + // Should work, as XK is is defined as special case (not officially ISO-3166-1,but used in the wild) + // See https://en.wikipedia.org/wiki/XK_(user_assigned_code) and https://github.com/codebude/QRCoder/issues/420 + country = "XK"; + exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); + Assert.Null(exception); + + + // Should throw exception, as ZZ isn't a valid country code + country = "ZZ"; + exception = Record.Exception(() => PayloadGenerator.SwissQrCode.Contact.WithStructuredAddress(name, zip, city, country)); + + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Country must be a valid \"two letter\" country code as defined by ISO 3166-1, but it isn't."); + } - [Fact] - [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_time_based_generates_with_standard_options() + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_time_based_generates_with_standard_options() + { + var pg = new PayloadGenerator.OneTimePassword { - var pg = new PayloadGenerator.OneTimePassword - { - Secret = "pwq6 5q55", - Issuer = "Google", - Label = "test@google.com", - }; - - pg.ToString().ShouldBe("otpauth://totp/Google:test%40google.com?secret=pwq65q55&issuer=Google"); - } + Secret = "pwq6 5q55", + Issuer = "Google", + Label = "test@google.com", + }; + + pg.ToString().ShouldBe("otpauth://totp/Google:test%40google.com?secret=pwq65q55&issuer=Google"); + } - [Fact] - [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_time_based_generates_with_standard_options_escapes_issuer_and_label() + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_time_based_generates_with_standard_options_escapes_issuer_and_label() + { + var pg = new PayloadGenerator.OneTimePassword { - var pg = new PayloadGenerator.OneTimePassword - { - Secret = "pwq6 5q55", - Issuer = "Google Google", - Label = "test/test@google.com", - }; - - pg.ToString().ShouldBe("otpauth://totp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google"); - } + Secret = "pwq6 5q55", + Issuer = "Google Google", + Label = "test/test@google.com", + }; + pg.ToString().ShouldBe("otpauth://totp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google"); + } - [Fact] - [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_hmac_based_generates_with_standard_options() - { - var pg = new PayloadGenerator.OneTimePassword - { - Secret = "pwq6 5q55", - Issuer = "Google", - Label = "test@google.com", - Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, - Counter = 500, - }; - - pg.ToString().ShouldBe("otpauth://hotp/Google:test%40google.com?secret=pwq65q55&issuer=Google&counter=500"); - } - [Fact] - [Category("PayloadGenerator/OneTimePassword")] - public void one_time_password_generator_hmac_based_generates_with_standard_options_escapes_issuer_and_label() + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_hmac_based_generates_with_standard_options() + { + var pg = new PayloadGenerator.OneTimePassword { - var pg = new PayloadGenerator.OneTimePassword - { - Secret = "pwq6 5q55", - Issuer = "Google Google", - Label = "test/test@google.com", - Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, - Counter = 500, - }; - - pg.ToString().ShouldBe("otpauth://hotp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google&counter=500"); - } + Secret = "pwq6 5q55", + Issuer = "Google", + Label = "test@google.com", + Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, + Counter = 500, + }; + pg.ToString().ShouldBe("otpauth://hotp/Google:test%40google.com?secret=pwq65q55&issuer=Google&counter=500"); + } - [Fact] - [Category("PayloadGenerator/ShadowSocksConfig")] - public void shadowsocks_generator_can_generate_payload() + [Fact] + [Category("PayloadGenerator/OneTimePassword")] + public void one_time_password_generator_hmac_based_generates_with_standard_options_escapes_issuer_and_label() + { + var pg = new PayloadGenerator.OneTimePassword { - var host = "192.168.2.5"; - var port = 1; - var password = "s3cr3t"; - var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method); - - generator - .ToString() - .ShouldBe("ss://cmM0LW1kNTpzM2NyM3RAMTkyLjE2OC4yLjU6MQ=="); - } + Secret = "pwq6 5q55", + Issuer = "Google Google", + Label = "test/test@google.com", + Type = PayloadGenerator.OneTimePassword.OneTimePasswordAuthType.HOTP, + Counter = 500, + }; - [Fact] - [Category("PayloadGenerator/ShadowSocksConfig")] - public void shadowsocks_generator_can_generate_payload_with_tag() - { - var host = "192.168.2.5"; - var port = 65535; - var password = "s3cr3t"; - var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - var tag = "server42"; - var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method, tag); - - generator - .ToString() - .ShouldBe("ss://cmM0LW1kNTpzM2NyM3RAMTkyLjE2OC4yLjU6NjU1MzU=#server42"); - } + pg.ToString().ShouldBe("otpauth://hotp/Google%20Google:test%2Ftest%40google.com?secret=pwq65q55&issuer=Google%20Google&counter=500"); + } - [Fact] - [Category("PayloadGenerator/ShadowSocksConfig")] - public void shadowsocks_generator_should_throw_portrange_low_exception() - { - var host = "192.168.2.5"; - var port = 0; - var password = "s3cr3t"; - var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; + [Fact] + [Category("PayloadGenerator/ShadowSocksConfig")] + public void shadowsocks_generator_can_generate_payload() + { + var host = "192.168.2.5"; + var port = 1; + var password = "s3cr3t"; + var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; + var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method); + + generator + .ToString() + .ShouldBe("ss://cmM0LW1kNTpzM2NyM3RAMTkyLjE2OC4yLjU6MQ=="); + } - var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); + [Fact] + [Category("PayloadGenerator/ShadowSocksConfig")] + public void shadowsocks_generator_can_generate_payload_with_tag() + { + var host = "192.168.2.5"; + var port = 65535; + var password = "s3cr3t"; + var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; + var tag = "server42"; + var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method, tag); + + generator + .ToString() + .ShouldBe("ss://cmM0LW1kNTpzM2NyM3RAMTkyLjE2OC4yLjU6NjU1MzU=#server42"); + } - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); - } + [Fact] + [Category("PayloadGenerator/ShadowSocksConfig")] + public void shadowsocks_generator_should_throw_portrange_low_exception() + { + var host = "192.168.2.5"; + var port = 0; + var password = "s3cr3t"; + var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - [Fact] - [Category("PayloadGenerator/ShadowSocksConfig")] - public void shadowsocks_generator_should_throw_portrange_high_exception() - { - var host = "192.168.2.5"; - var port = 65536; - var password = "s3cr3t"; - var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; + var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); - var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); + } - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); - } + [Fact] + [Category("PayloadGenerator/ShadowSocksConfig")] + public void shadowsocks_generator_should_throw_portrange_high_exception() + { + var host = "192.168.2.5"; + var port = 65536; + var password = "s3cr3t"; + var method = PayloadGenerator.ShadowSocksConfig.Method.Rc4Md5; - [Fact] - [Category("PayloadGenerator/ShadowSocksConfig")] - public void shadowsocks_generator_can_generate_payload_with_plugin() - { - var host = "192.168.100.1"; - var port = 8888; - var password = "test"; - var method = PayloadGenerator.ShadowSocksConfig.Method.BfCfb; - var plugin = "obfs-local"; - var pluginOption = "obfs=http;obfs-host=google.com"; - var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method, plugin, pluginOption); - - generator - .ToString() - .ShouldBe("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"); - } + var exception = Record.Exception(() => new PayloadGenerator.ShadowSocksConfig(host, port, password, method)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Value of 'port' must be within 0 and 65535."); + } - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_simple_mecard() - { - var firstname = "John"; - var lastname = "Doe"; - var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname); + [Fact] + [Category("PayloadGenerator/ShadowSocksConfig")] + public void shadowsocks_generator_can_generate_payload_with_plugin() + { + var host = "192.168.100.1"; + var port = 8888; + var password = "test"; + var method = PayloadGenerator.ShadowSocksConfig.Method.BfCfb; + var plugin = "obfs-local"; + var pluginOption = "obfs=http;obfs-host=google.com"; + var generator = new PayloadGenerator.ShadowSocksConfig(host, port, password, method, plugin, pluginOption); + + generator + .ToString() + .ShouldBe("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888/?plugin=obfs-local%3bobfs%3dhttp%3bobfs-host%3dgoogle.com"); + } - generator - .ToString() - .ShouldBe("MECARD+\r\nN:Doe, John\r\nADR:,,,,,,"); - } - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_mecard() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("MECARD+\r\nN:Doe, John\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL:+4253212222\r\nTEL:+421701234567\r\nTEL:+4253211337\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nBDAY:19700201\r\nADR:,,Long street 42,12345,Super-Town,,Starlight Country\r\nURL:http://john.doe\r\nNICKNAME:Johnny"); - } + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_simple_mecard() + { + var firstname = "John"; + var lastname = "Doe"; + var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_mecard_reversed() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, addressOrder: PayloadGenerator.ContactData.AddressOrder.Reversed, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("MECARD+\r\nN:Doe, John\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL:+4253212222\r\nTEL:+421701234567\r\nTEL:+4253211337\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nBDAY:19700201\r\nADR:,,42 Long street,Super-Town,,12345,Starlight Country\r\nURL:http://john.doe\r\nNICKNAME:Johnny"); - } + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname); - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_vcard21() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard21; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("BEGIN:VCARD\r\nVERSION:2.1\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;HOME;VOICE:+4253212222\r\nTEL;HOME;CELL:+421701234567\r\nTEL;WORK;VOICE:+4253211337\r\nADR;HOME;PREF:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nEND:VCARD"); - } + generator + .ToString() + .ShouldBe("MECARD+\r\nN:Doe, John\r\nADR:,,,,,,"); + } - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_vcard3() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard3; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("BEGIN:VCARD\r\nVERSION:3.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=HOME,VOICE:+4253212222\r\nTEL;TYPE=HOME,CELL:+421701234567\r\nTEL;TYPE=WORK,VOICE:+4253211337\r\nADR;TYPE=HOME,PREF:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); - } + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_mecard() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("MECARD+\r\nN:Doe, John\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL:+4253212222\r\nTEL:+421701234567\r\nTEL:+4253211337\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nBDAY:19700201\r\nADR:,,Long street 42,12345,Super-Town,,Starlight Country\r\nURL:http://john.doe\r\nNICKNAME:Johnny"); + } - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_vcard4() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard4; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=home,voice;VALUE=uri:tel:+4253212222\r\nTEL;TYPE=home,cell;VALUE=uri:tel:+421701234567\r\nTEL;TYPE=work,voice;VALUE=uri:tel:+4253211337\r\nADR;TYPE=home,pref:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); - } + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_mecard_reversed() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.MeCard; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, addressOrder: PayloadGenerator.ContactData.AddressOrder.Reversed, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("MECARD+\r\nN:Doe, John\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL:+4253212222\r\nTEL:+421701234567\r\nTEL:+4253211337\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nBDAY:19700201\r\nADR:,,42 Long street,Super-Town,,12345,Starlight Country\r\nURL:http://john.doe\r\nNICKNAME:Johnny"); + } - [Fact] - [Category("PayloadGenerator/ContactData")] - public void contactdata_generator_can_generate_payload_full_vcard4_reverse() - { - var firstname = "John"; - var lastname = "Doe"; - var nickname = "Johnny"; - var org = "Johnny's Badass Programming"; - var orgTitle = "Badass Manager"; - var phone = "+4253212222"; - var mobilePhone = "+421701234567"; - var workPhone = "+4253211337"; - var email = "me@john.doe"; - var birthday = new DateTime(1970, 02, 01); - var website = "http://john.doe"; - var street = "Long street"; - var houseNumber = "42"; - var city = "Super-Town"; - var zipCode = "12345"; - var country = "Starlight Country"; - var note = "Badass programmer."; - var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard4; - - var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, addressOrder: PayloadGenerator.ContactData.AddressOrder.Reversed, org: org, orgTitle: orgTitle); - - generator - .ToString() - .ShouldBe("BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=home,voice;VALUE=uri:tel:+4253212222\r\nTEL;TYPE=home,cell;VALUE=uri:tel:+421701234567\r\nTEL;TYPE=work,voice;VALUE=uri:tel:+4253211337\r\nADR;TYPE=home,pref:;;42 Long street;Super-Town;;12345;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); - } + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_vcard21() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard21; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("BEGIN:VCARD\r\nVERSION:2.1\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;HOME;VOICE:+4253212222\r\nTEL;HOME;CELL:+421701234567\r\nTEL;WORK;VOICE:+4253211337\r\nADR;HOME;PREF:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nEND:VCARD"); + } - [Fact] - [Category("PayloadGenerator/WhatsAppMessage")] - public void whatsapp_generator_can_generate_payload_simple() - { - var number = "491601234567"; - var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; - var generator = new PayloadGenerator.WhatsAppMessage(number, msg); + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_vcard3() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard3; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("BEGIN:VCARD\r\nVERSION:3.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=HOME,VOICE:+4253212222\r\nTEL;TYPE=HOME,CELL:+421701234567\r\nTEL;TYPE=WORK,VOICE:+4253211337\r\nADR;TYPE=HOME,PREF:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); + } - generator - .ToString() - .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); - } + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_vcard4() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard4; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=home,voice;VALUE=uri:tel:+4253212222\r\nTEL;TYPE=home,cell;VALUE=uri:tel:+421701234567\r\nTEL;TYPE=work,voice;VALUE=uri:tel:+4253211337\r\nADR;TYPE=home,pref:;;Long street 42;12345;Super-Town;;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); + } - [Fact] - [Category("PayloadGenerator/WhatsAppMessage")] - public void whatsapp_should_add_unused_params() - { - var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; - var generator = new PayloadGenerator.WhatsAppMessage(msg); + [Fact] + [Category("PayloadGenerator/ContactData")] + public void contactdata_generator_can_generate_payload_full_vcard4_reverse() + { + var firstname = "John"; + var lastname = "Doe"; + var nickname = "Johnny"; + var org = "Johnny's Badass Programming"; + var orgTitle = "Badass Manager"; + var phone = "+4253212222"; + var mobilePhone = "+421701234567"; + var workPhone = "+4253211337"; + var email = "me@john.doe"; + var birthday = new DateTime(1970, 02, 01); + var website = "http://john.doe"; + var street = "Long street"; + var houseNumber = "42"; + var city = "Super-Town"; + var zipCode = "12345"; + var country = "Starlight Country"; + var note = "Badass programmer."; + var outputType = PayloadGenerator.ContactData.ContactOutputType.VCard4; + + var generator = new PayloadGenerator.ContactData(outputType, firstname, lastname, nickname, phone, mobilePhone, workPhone, email, birthday, website, street, houseNumber, city, zipCode, country, note, addressOrder: PayloadGenerator.ContactData.AddressOrder.Reversed, org: org, orgTitle: orgTitle); + + generator + .ToString() + .ShouldBe("BEGIN:VCARD\r\nVERSION:4.0\r\nN:Doe;John;;;\r\nFN:John Doe\r\nORG:Johnny's Badass Programming\r\nTITLE:Badass Manager\r\nTEL;TYPE=home,voice;VALUE=uri:tel:+4253212222\r\nTEL;TYPE=home,cell;VALUE=uri:tel:+421701234567\r\nTEL;TYPE=work,voice;VALUE=uri:tel:+4253211337\r\nADR;TYPE=home,pref:;;42 Long street;Super-Town;;12345;Starlight Country\r\nBDAY:19700201\r\nURL:http://john.doe\r\nEMAIL:me@john.doe\r\nNOTE:Badass programmer.\r\nNICKNAME:Johnny\r\nEND:VCARD"); + } - generator - .ToString() - .ShouldBe("https://wa.me/?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); - } + [Fact] + [Category("PayloadGenerator/WhatsAppMessage")] + public void whatsapp_generator_can_generate_payload_simple() + { + var number = "491601234567"; + var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; + var generator = new PayloadGenerator.WhatsAppMessage(number, msg); - [Fact] - [Category("PayloadGenerator/WhatsAppMessage")] - public void whatsapp_should_cleanup_phonenumber_1() - { - var number = "+49(160)1234567"; - var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; - var generator = new PayloadGenerator.WhatsAppMessage(number, msg); + generator + .ToString() + .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); + } - generator - .ToString() - .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); - } + [Fact] + [Category("PayloadGenerator/WhatsAppMessage")] + public void whatsapp_should_add_unused_params() + { + var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; + var generator = new PayloadGenerator.WhatsAppMessage(msg); - [Fact] - [Category("PayloadGenerator/WhatsAppMessage")] - public void whatsapp_should_cleanup_phonenumber_2() - { - var number = "0049-160-1234 567"; - var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; - var generator = new PayloadGenerator.WhatsAppMessage(number, msg); + generator + .ToString() + .ShouldBe("https://wa.me/?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); + } - generator - .ToString() - .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); - } + [Fact] + [Category("PayloadGenerator/WhatsAppMessage")] + public void whatsapp_should_cleanup_phonenumber_1() + { + var number = "+49(160)1234567"; + var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; + var generator = new PayloadGenerator.WhatsAppMessage(number, msg); - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_can_generate_payload_simple() - { - var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; - var generator = new PayloadGenerator.MoneroTransaction(address); + generator + .ToString() + .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); + } - generator - .ToString() - .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"); - } + [Fact] + [Category("PayloadGenerator/WhatsAppMessage")] + public void whatsapp_should_cleanup_phonenumber_2() + { + var number = "0049-160-1234 567"; + var msg = "This is a sample message with Umlauts: Ä,ö, ü and ß."; + var generator = new PayloadGenerator.WhatsAppMessage(number, msg); - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_can_generate_payload_first_param() - { - var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; - var amount = 1.3f; - var generator = new PayloadGenerator.MoneroTransaction(address, amount); + generator + .ToString() + .ShouldBe("https://wa.me/491601234567?text=This%20is%20a%20sample%20message%20with%20Umlauts%3A%20%C3%84%2C%C3%B6%2C%20%C3%BC%20and%20%C3%9F."); + } - generator - .ToString() - .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_amount=1.3"); - } + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_can_generate_payload_simple() + { + var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; + var generator = new PayloadGenerator.MoneroTransaction(address); + generator + .ToString() + .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"); + } - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_can_generate_payload_named_param() - { - var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; - var recipient = "Raffael Herrmann"; - var generator = new PayloadGenerator.MoneroTransaction(address, recipientName: recipient); + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_can_generate_payload_first_param() + { + var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; + var amount = 1.3f; + var generator = new PayloadGenerator.MoneroTransaction(address, amount); - generator - .ToString() - .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?recipient_name=Raffael%20Herrmann"); - } + generator + .ToString() + .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_amount=1.3"); + } - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_can_generate_payload_full_param() - { - var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; - var amount = 1.3f; - var paymentId = "1234567890123456789012345678901234567890123456789012345678901234"; - var recipient = "Raffael Herrmann"; - var description = "Monero transaction via QrCoder.NET."; - var generator = new PayloadGenerator.MoneroTransaction(address, amount, paymentId, recipient, description); - - generator - .ToString() - .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_payment_id=1234567890123456789012345678901234567890123456789012345678901234&recipient_name=Raffael%20Herrmann&tx_amount=1.3&tx_description=Monero%20transaction%20via%20QrCoder.NET."); - } + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_can_generate_payload_named_param() + { + var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; + var recipient = "Raffael Herrmann"; + var generator = new PayloadGenerator.MoneroTransaction(address, recipientName: recipient); + generator + .ToString() + .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?recipient_name=Raffael%20Herrmann"); + } - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_should_throw_wrong_amount_exception() - { - var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; - var amount = -1f; - var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address, amount)); + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_can_generate_payload_full_param() + { + var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; + var amount = 1.3f; + var paymentId = "1234567890123456789012345678901234567890123456789012345678901234"; + var recipient = "Raffael Herrmann"; + var description = "Monero transaction via QrCoder.NET."; + var generator = new PayloadGenerator.MoneroTransaction(address, amount, paymentId, recipient, description); + + generator + .ToString() + .ShouldBe("monero://46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_payment_id=1234567890123456789012345678901234567890123456789012345678901234&recipient_name=Raffael%20Herrmann&tx_amount=1.3&tx_description=Monero%20transaction%20via%20QrCoder.NET."); + } - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("Value of 'txAmount' must be greater than 0."); - } + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_should_throw_wrong_amount_exception() + { + var address = "46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em"; + var amount = -1f; - [Fact] - [Category("PayloadGenerator/Monero")] - public void monero_generator_should_throw_no_address_exception() - { - var address = ""; + var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address, amount)); - var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("Value of 'txAmount' must be greater than 0."); + } - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The address is mandatory and has to be set."); - } + [Fact] + [Category("PayloadGenerator/Monero")] + public void monero_generator_should_throw_no_address_exception() + { + var address = ""; - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_mandatory_fields() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810965770000413"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|"); - } + var exception = Record.Exception(() => new PayloadGenerator.MoneroTransaction(address)); - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_encoding_win1251() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810965770000413"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.windows_1251); - - byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 49, 124, 78, 97, 109, 101, 61, 206, 206, 206, 32, 171, 210, 240, 232, 32, 234, 232, 242, 224, 187, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 206, 192, 206, 32, 34, 193, 192, 205, 202, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; - var payloadBytes = generator.ToBytes(); - - Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); - for (int i = 0; i < targetBytes.Length; i++) - { - Assert.True(targetBytes[i] == payloadBytes[i], - $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." - ); - } - } + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The address is mandatory and has to be set."); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_encoding_koi8() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810965770000413"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.koi8_r); - - byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 51, 124, 78, 97, 109, 101, 61, 239, 239, 239, 32, 60, 244, 210, 201, 32, 203, 201, 212, 193, 62, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 239, 225, 239, 32, 34, 226, 225, 238, 235, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; - var payloadBytes = generator.ToBytes(); - - Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); - for (int i = 0; i < targetBytes.Length; i++) - { - Assert.True(targetBytes[i] == payloadBytes[i], - $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." - ); - } - } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_custom_separator() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО | \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - - generator - .ToString() - .ShouldBe($"ST00012#Name={name}#PersonalAcc={account}#BankName={bankName}#BIC={bic}#CorrespAcc={correspAcc}#"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_mandatory_fields() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810965770000413"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|"); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_should_throw_no_separator_exception() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО | \"БАНК\""; - var name = "|@;:^_~{}!#$%&()*+,/"; //All chars that could be used as separator - var correspAcc = "30101810400000000225"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - - var exception = Record.Exception(() => generator.ToString()); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("No valid separator found."); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_encoding_win1251() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810965770000413"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.windows_1251); - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_should_throw_data_too_long_exception() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "A very very very very very long bank name"; - // We use € symbol for the test case, because it needs 2-bytes. Otherwise we couldn't generate more than 300 bytes - // of mandatory data to trigger the test case and stay at the same time within the 160 chars field validation limit - var name = "A very €€€€ €€€€ €€€€ €€€€ very very very very very very very very very very very very very very very very very very very very very very very very ver long name"; - var correspAcc = "30101810400000000225"; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); - - var exception = Record.Exception(() => generator.ToString()); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldStartWith("Data too long"); - } + byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 49, 124, 78, 97, 109, 101, 61, 206, 206, 206, 32, 171, 210, 240, 232, 32, 234, 232, 242, 224, 187, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 206, 192, 206, 32, 34, 193, 192, 205, 202, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; + var payloadBytes = generator.ToBytes(); - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_should_throw_no_data_too_long_exception() + Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); + for (int i = 0; i < targetBytes.Length; i++) { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "ОАО | \"БАНК\""; - var name = "A name"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - FirstName = "Another long long long long long long long long long long long long long long firstname", - LastName = "Another long long long long long long long long long long long long long long lastname", - Category = "A pretty long long long long long long long long long long long long long category", - Sum = "125000" - }; - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - // Should throw no exception as the 300 byte limit applies only to the mandatory fields - // See https://github.com/codebude/QRCoder/issues/392 - var exception = Record.Exception(() => generator.ToString()); - Assert.Null(exception); + Assert.True(targetBytes[i] == payloadBytes[i], + $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." + ); } + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_should_throw_must_not_be_null_exception() - { - string account = null; - var bic = "044525225"; - var bankName = "ОАО | \"БАНК\""; - var name = "|@;:^_~{}!#$%&()*+,/"; - var correspAcc = "30101810400000000225"; - - var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe($"The input for 'PersonalAcc' must not be null."); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_encoding_koi8() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810965770000413"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, null, PayloadGenerator.RussiaPaymentOrder.CharacterSets.koi8_r); - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_should_throw_unmatched_pattern_exception() - { - string account = "40702810138250123017"; - var bic = "abcd"; //Invalid BIC - var bankName = "ОАО | \"БАНК\""; - var name = "|@;:^_~{}!#$%&()*+,/"; - var correspAcc = "30101810400000000225"; - - var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); - Assert.NotNull(exception); - Assert.IsType(exception); - exception.Message.ShouldBe("The input for 'BIC' (abcd) doesn't match the pattern ^\\d{9}$"); - } + byte[] targetBytes = new byte[] { 83, 84, 48, 48, 48, 49, 51, 124, 78, 97, 109, 101, 61, 239, 239, 239, 32, 60, 244, 210, 201, 32, 203, 201, 212, 193, 62, 124, 80, 101, 114, 115, 111, 110, 97, 108, 65, 99, 99, 61, 52, 48, 55, 48, 50, 56, 49, 48, 49, 51, 56, 50, 53, 48, 49, 50, 51, 48, 49, 55, 124, 66, 97, 110, 107, 78, 97, 109, 101, 61, 239, 225, 239, 32, 34, 226, 225, 238, 235, 34, 124, 66, 73, 67, 61, 48, 52, 52, 53, 50, 53, 50, 50, 53, 124, 67, 111, 114, 114, 101, 115, 112, 65, 99, 99, 61, 51, 48, 49, 48, 49, 56, 49, 48, 57, 54, 53, 55, 55, 48, 48, 48, 48, 52, 49, 51, 124 }; + var payloadBytes = generator.ToBytes(); - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_some_additional_fields() + Assert.True(targetBytes.Length == payloadBytes.Length, $"Byte array lengths different. Expected: {targetBytes.Length}, Actual: {payloadBytes.Length}"); + for (int i = 0; i < targetBytes.Length; i++) { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - FirstName = "Raffael", - LastName = "Herrmann", - Sum = "125000" - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Sum={optionalFields.Sum}|LastName={optionalFields.LastName}|FirstName={optionalFields.FirstName}|"); + Assert.True(targetBytes[i] == payloadBytes[i], + $"Expected: '{targetBytes[i]}', Actual: '{payloadBytes[i]}' at offset {i}." + ); } + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_all_additional_fields_pt1() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - FirstName = "R", - MiddleName = "C", - LastName = "Hann", - Sum = "1250", - AddAmount = "10", - BirthDate = new DateTime(1990, 1, 1), - Category = "1", - CBC = "CBC1", - ChildFio = "J Doe", - ClassNum = "1", - Contract = "99", - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Sum={optionalFields.Sum}|CBC=CBC1|LastName=Hann|FirstName=R|MiddleName=C|Contract=99|ChildFio=J Doe|BirthDate=01.01.1990|Category=1|ClassNum=1|AddAmount=10|"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_custom_separator() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО | \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); + + generator + .ToString() + .ShouldBe($"ST00012#Name={name}#PersonalAcc={account}#BankName={bankName}#BIC={bic}#CorrespAcc={correspAcc}#"); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_all_additional_fields_pt2() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - CounterId = "1234", - CounterVal = "9999", - DocDate = new DateTime(2021, 11, 8), - DocIdx = "A1", - DocNo = "11", - DrawerStatus = "D1", - ExecId = "77", - Flat = "5a", - InstNum = "987", - KPP = "KPP1", - OKTMO = "112233" - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|DrawerStatus=D1|KPP=KPP1|OKTMO=112233|DocNo=11|DocDate=08.11.2021|DocIdx=A1|Flat=5a|CounterId=1234|CounterVal=9999|InstNum=987|ExecId=77|"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_should_throw_no_separator_exception() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО | \"БАНК\""; + var name = "|@;:^_~{}!#$%&()*+,/"; //All chars that could be used as separator + var correspAcc = "30101810400000000225"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); + + var exception = Record.Exception(() => generator.ToString()); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("No valid separator found."); + } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_should_throw_data_too_long_exception() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "A very very very very very long bank name"; + // We use € symbol for the test case, because it needs 2-bytes. Otherwise we couldn't generate more than 300 bytes + // of mandatory data to trigger the test case and stay at the same time within the 160 chars field validation limit + var name = "A very €€€€ €€€€ €€€€ €€€€ very very very very very very very very very very very very very very very very very very very very very very very very ver long name"; + var correspAcc = "30101810400000000225"; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc); + + var exception = Record.Exception(() => generator.ToString()); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldStartWith("Data too long"); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_all_additional_fields_pt3() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - PayeeINN = "INN1", - PayerAddress = "Street 1, 123 City", - PayerIdNum = "555", - PayerIdType = "X", - PayerINN = "INN2", - PaymPeriod = "12", - PaymTerm = "A", - PaytReason = "01", - PensAcc = "SNILS_NO" - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|PayeeINN=INN1|PayerINN=INN2|PaytReason=01|PayerAddress=Street 1, 123 City|PensAcc=SNILS_NO|PayerIdType=X|PayerIdNum=555|PaymTerm=A|PaymPeriod=12|"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_should_throw_no_data_too_long_exception() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "ОАО | \"БАНК\""; + var name = "A name"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + FirstName = "Another long long long long long long long long long long long long long long firstname", + LastName = "Another long long long long long long long long long long long long long long lastname", + Category = "A pretty long long long long long long long long long long long long long category", + Sum = "125000" + }; + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + // Should throw no exception as the 300 byte limit applies only to the mandatory fields + // See https://github.com/codebude/QRCoder/issues/392 + var exception = Record.Exception(() => generator.ToString()); + Assert.Null(exception); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_all_additional_fields_pt4() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - PersAcc = "2222", - PersonalAccount = "3333", - Phone = "0012345", - Purpose = "Test", - QuittDate = new DateTime(2021, 2, 1), - QuittId = "7", - RegType = "y", - RuleId = "2", - ServiceName = "Bank" - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Purpose=Test|PersonalAccount=3333|PersAcc=2222|Phone=0012345|ServiceName=Bank|QuittId=7|QuittDate=01.02.2021|RuleId=2|RegType=y|"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_should_throw_must_not_be_null_exception() + { + string account = null; + var bic = "044525225"; + var bankName = "ОАО | \"БАНК\""; + var name = "|@;:^_~{}!#$%&()*+,/"; + var correspAcc = "30101810400000000225"; + + var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe($"The input for 'PersonalAcc' must not be null."); + } - [Fact] - [Category("PayloadGenerator/RussiaPaymentOrder")] - public void russiapayment_generator_can_generate_payload_all_additional_fields_pt5() - { - var account = "40702810138250123017"; - var bic = "044525225"; - var bankName = "=ОАО \"БАНК\""; - var name = "ООО «Три кита»"; - var correspAcc = "30101810400000000225"; - var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() - { - SpecFio = "T. Eacher", - TaxPaytKind = "99", - TaxPeriod = "31", - TechCode = PayloadGenerator.RussiaPaymentOrder.TechCode.ГИБДД_налоги_пошлины_бюджетные_платежи, - UIN = "1a2b" - }; - - var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); - - generator - .ToString() - .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|TaxPeriod=31|TaxPaytKind=99|SpecFio=T. Eacher|UIN=1a2b|TechCode=ГИБДД_налоги_пошлины_бюджетные_платежи|"); - } + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_should_throw_unmatched_pattern_exception() + { + string account = "40702810138250123017"; + var bic = "abcd"; //Invalid BIC + var bankName = "ОАО | \"БАНК\""; + var name = "|@;:^_~{}!#$%&()*+,/"; + var correspAcc = "30101810400000000225"; + + var exception = Record.Exception(() => new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc)); + Assert.NotNull(exception); + Assert.IsType(exception); + exception.Message.ShouldBe("The input for 'BIC' (abcd) doesn't match the pattern ^\\d{9}$"); + } + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_some_additional_fields() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + FirstName = "Raffael", + LastName = "Herrmann", + Sum = "125000" + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Sum={optionalFields.Sum}|LastName={optionalFields.LastName}|FirstName={optionalFields.FirstName}|"); + } + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_all_additional_fields_pt1() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + FirstName = "R", + MiddleName = "C", + LastName = "Hann", + Sum = "1250", + AddAmount = "10", + BirthDate = new DateTime(1990, 1, 1), + Category = "1", + CBC = "CBC1", + ChildFio = "J Doe", + ClassNum = "1", + Contract = "99", + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Sum={optionalFields.Sum}|CBC=CBC1|LastName=Hann|FirstName=R|MiddleName=C|Contract=99|ChildFio=J Doe|BirthDate=01.01.1990|Category=1|ClassNum=1|AddAmount=10|"); + } + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_all_additional_fields_pt2() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + CounterId = "1234", + CounterVal = "9999", + DocDate = new DateTime(2021, 11, 8), + DocIdx = "A1", + DocNo = "11", + DrawerStatus = "D1", + ExecId = "77", + Flat = "5a", + InstNum = "987", + KPP = "KPP1", + OKTMO = "112233" + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|DrawerStatus=D1|KPP=KPP1|OKTMO=112233|DocNo=11|DocDate=08.11.2021|DocIdx=A1|Flat=5a|CounterId=1234|CounterVal=9999|InstNum=987|ExecId=77|"); + } + + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_all_additional_fields_pt3() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + PayeeINN = "INN1", + PayerAddress = "Street 1, 123 City", + PayerIdNum = "555", + PayerIdType = "X", + PayerINN = "INN2", + PaymPeriod = "12", + PaymTerm = "A", + PaytReason = "01", + PensAcc = "SNILS_NO" + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|PayeeINN=INN1|PayerINN=INN2|PaytReason=01|PayerAddress=Street 1, 123 City|PensAcc=SNILS_NO|PayerIdType=X|PayerIdNum=555|PaymTerm=A|PaymPeriod=12|"); + } + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_all_additional_fields_pt4() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + PersAcc = "2222", + PersonalAccount = "3333", + Phone = "0012345", + Purpose = "Test", + QuittDate = new DateTime(2021, 2, 1), + QuittId = "7", + RegType = "y", + RuleId = "2", + ServiceName = "Bank" + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|Purpose=Test|PersonalAccount=3333|PersAcc=2222|Phone=0012345|ServiceName=Bank|QuittId=7|QuittDate=01.02.2021|RuleId=2|RegType=y|"); + } + + [Fact] + [Category("PayloadGenerator/RussiaPaymentOrder")] + public void russiapayment_generator_can_generate_payload_all_additional_fields_pt5() + { + var account = "40702810138250123017"; + var bic = "044525225"; + var bankName = "=ОАО \"БАНК\""; + var name = "ООО «Три кита»"; + var correspAcc = "30101810400000000225"; + var optionalFields = new PayloadGenerator.RussiaPaymentOrder.OptionalFields() + { + SpecFio = "T. Eacher", + TaxPaytKind = "99", + TaxPeriod = "31", + TechCode = PayloadGenerator.RussiaPaymentOrder.TechCode.ГИБДД_налоги_пошлины_бюджетные_платежи, + UIN = "1a2b" + }; + + var generator = new PayloadGenerator.RussiaPaymentOrder(name, account, bankName, bic, correspAcc, optionalFields); + + generator + .ToString() + .ShouldBe($"ST00012|Name={name}|PersonalAcc={account}|BankName={bankName}|BIC={bic}|CorrespAcc={correspAcc}|TaxPeriod=31|TaxPaytKind=99|SpecFio=T. Eacher|UIN=1a2b|TechCode=ГИБДД_налоги_пошлины_бюджетные_платежи|"); } } diff --git a/QRCoderTests/PngByteQRCodeRendererTests.cs b/QRCoderTests/PngByteQRCodeRendererTests.cs index 7fc1a7ce..82cabf4a 100644 --- a/QRCoderTests/PngByteQRCodeRendererTests.cs +++ b/QRCoderTests/PngByteQRCodeRendererTests.cs @@ -1,4 +1,4 @@ -using Xunit; +using Xunit; using QRCoder; using Shouldly; using QRCoderTests.Helpers.XUnitExtenstions; @@ -8,160 +8,147 @@ using System.IO; #endif -namespace QRCoderTests +namespace QRCoderTests; + +/**************************************************************************************************** + * Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare. + * This is necessary, because the Deflate implementation differs on the different target + * platforms and thus the outcome, even if visually identical, differs. Thus only a visual + * test method makes sense. In addition bytewise differences shouldn't be important, if the + * visual outcome is identical and thus the qr code is identical/scannable. + ****************************************************************************************************/ +public class PngByteQRCodeRendererTests { - /**************************************************************************************************** - * Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare. - * This is necessary, because the Deflate implementation differs on the different target - * platforms and thus the outcome, even if visually identical, differs. Thus only a visual - * test method makes sense. In addition bytewise differences shouldn't be important, if the - * visual outcome is identical and thus the qr code is identical/scannable. - ****************************************************************************************************/ - public class PngByteQRCodeRendererTests - { - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_blackwhite() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5); + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_blackwhite() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("1fc35c3bea6fad47427143ce716c83b8"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("1fc35c3bea6fad47427143ce716c83b8"); #else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("0cfc8a8d552ade875190d8e9f5c1e1bf"); - } + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("0cfc8a8d552ade875190d8e9f5c1e1bf"); #endif - } + } - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_color() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_color() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 }); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7"); #else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("88d394b2405499869feb69b81593e703"); - } + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("88d394b2405499869feb69b81593e703"); #endif - } + } - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_color_with_alpha() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }); + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_color_with_alpha() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); #else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); - } + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); #endif - } + } - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_color_without_quietzones() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, false); + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_color_without_quietzones() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, false); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("07f760b3eb54901840b094d31e299713"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("07f760b3eb54901840b094d31e299713"); #else - File.WriteAllBytes(@"C:\Temp\pngbyte_35.png", pngCodeGfx); - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - bmp.MakeTransparent(Color.Transparent); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("825a6469f89bf9e3d7318a5390d5ba7f"); - } + File.WriteAllBytes(@"C:\Temp\pngbyte_35.png", pngCodeGfx); + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + bmp.MakeTransparent(Color.Transparent); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("825a6469f89bf9e3d7318a5390d5ba7f"); #endif - } - - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_instantate_pngbyte_qrcode_parameterless() - { - var pngCode = new PngByteQRCode(); - pngCode.ShouldNotBeNull(); - pngCode.ShouldBeOfType(); - } - - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_from_helper() - { - //Create QR code - var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L, 10); + } + + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_instantate_pngbyte_qrcode_parameterless() + { + var pngCode = new PngByteQRCode(); + pngCode.ShouldNotBeNull(); + pngCode.ShouldBeOfType(); + } + + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_from_helper() + { + //Create QR code + var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L, 10); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("c562388f4f3cf13a299b469a3e3b852f"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("c562388f4f3cf13a299b469a3e3b852f"); #else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("a2ea116068eb516a7c210b2541e99348"); - } + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("a2ea116068eb516a7c210b2541e99348"); #endif - } + } - [Fact] - [Category("QRRenderer/PngByteQRCode")] - public void can_render_pngbyte_qrcode_from_helper_2() - { - //Create QR code - var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", 5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, QRCodeGenerator.ECCLevel.L); + [Fact] + [Category("QRRenderer/PngByteQRCode")] + public void can_render_pngbyte_qrcode_from_helper_2() + { + //Create QR code + var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", 5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, QRCodeGenerator.ECCLevel.L); #if NETCOREAPP1_1 - var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); - result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); + var result = HelperFunctions.ByteArrayToHash(pngCodeGfx); + result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5"); #else - using (var mStream = new MemoryStream(pngCodeGfx)) - { - var bmp = (Bitmap)Image.FromStream(mStream); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); - } + using var mStream = new MemoryStream(pngCodeGfx); + var bmp = (Bitmap)Image.FromStream(mStream); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("1d81b3d52fc64543186558eee7d9494b"); #endif - } - } + } diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.cs b/QRCoderTests/PostscriptQRCodeRendererTests.cs index a71db527..4d76ee15 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.cs +++ b/QRCoderTests/PostscriptQRCodeRendererTests.cs @@ -1,91 +1,90 @@ -#if !NETCOREAPP1_1 -using QRCoder; -using QRCoderTests.Helpers; -using QRCoderTests.Helpers.XUnitExtenstions; -using Shouldly; +#if !NETCOREAPP1_1 using System; using System.Drawing; using System.IO; using System.Text.RegularExpressions; +using QRCoder; +using QRCoderTests.Helpers; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; using Xunit; -namespace QRCoderTests +namespace QRCoderTests; + +public class PostscriptQRCodeRendererTests { - public class PostscriptQRCodeRendererTests + [Fact] + [Category("QRRenderer/PostscriptQRCode")] + public void can_render_postscript_qrcode_simple() { - [Fact] - [Category("QRRenderer/PostscriptQRCode")] - public void can_render_postscript_qrcode_simple() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var ps = new PostscriptQRCode(data).GetGraphic(5); + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var ps = new PostscriptQRCode(data).GetGraphic(5); - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("06b90d1e64bf022a248453e5f91101a0"); - } + var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); + result.ShouldBe("06b90d1e64bf022a248453e5f91101a0"); + } - [Fact] - [Category("QRRenderer/PostscriptQRCode")] - public void can_render_postscript_qrcode_eps() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var ps = new PostscriptQRCode(data).GetGraphic(5, true); + [Fact] + [Category("QRRenderer/PostscriptQRCode")] + public void can_render_postscript_qrcode_eps() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var ps = new PostscriptQRCode(data).GetGraphic(5, true); - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("50f6152cdb0b685595d80e7888712d3b"); - } + var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); + result.ShouldBe("50f6152cdb0b685595d80e7888712d3b"); + } - [Fact] - [Category("QRRenderer/PostscriptQRCode")] - public void can_render_postscript_qrcode_size() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var ps = new PostscriptQRCode(data).GetGraphic(new Size(33, 33)); + [Fact] + [Category("QRRenderer/PostscriptQRCode")] + public void can_render_postscript_qrcode_size() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var ps = new PostscriptQRCode(data).GetGraphic(new Size(33, 33)); - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("49c7faaafef312eb4b6ea1fec195e63d"); - } + var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); + result.ShouldBe("49c7faaafef312eb4b6ea1fec195e63d"); + } - [Fact] - [Category("QRRenderer/PostscriptQRCode")] - public void can_render_postscript_qrcode_size_no_quiet_zones() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var ps = new PostscriptQRCode(data).GetGraphic(new Size(50, 50), false); + [Fact] + [Category("QRRenderer/PostscriptQRCode")] + public void can_render_postscript_qrcode_size_no_quiet_zones() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var ps = new PostscriptQRCode(data).GetGraphic(new Size(50, 50), false); - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("9bfa0468e125d9815a39902133a10762"); - } + var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); + result.ShouldBe("9bfa0468e125d9815a39902133a10762"); + } - [Fact] - [Category("QRRenderer/PostscriptQRCode")] - public void can_render_postscript_qrcode_colors() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var ps = new PostscriptQRCode(data).GetGraphic(5, Color.Red, Color.Blue); + [Fact] + [Category("QRRenderer/PostscriptQRCode")] + public void can_render_postscript_qrcode_colors() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var ps = new PostscriptQRCode(data).GetGraphic(5, Color.Red, Color.Blue); - var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); - result.ShouldBe("2e001d7f67a446eb1b5df32ff5321808"); - } + var result = HelperFunctions.StringToHash(RemoveCreationDate(ps)); + result.ShouldBe("2e001d7f67a446eb1b5df32ff5321808"); + } - private static string RemoveCreationDate(string text) - { - // Regex pattern to match lines that start with %%CreationDate: followed by any characters until the end of the line - string pattern = @"%%CreationDate:.*\r?\n?"; + private static string RemoveCreationDate(string text) + { + // Regex pattern to match lines that start with %%CreationDate: followed by any characters until the end of the line + string pattern = @"%%CreationDate:.*\r?\n?"; - // Use Regex.Replace to remove matching lines - return Regex.Replace(text, pattern, string.Empty, RegexOptions.Multiline); - } + // Use Regex.Replace to remove matching lines + return Regex.Replace(text, pattern, string.Empty, RegexOptions.Multiline); } } -#endif \ No newline at end of file +#endif diff --git a/QRCoderTests/QRCodeRendererTests.cs b/QRCoderTests/QRCodeRendererTests.cs index d01eaf7a..5576e394 100644 --- a/QRCoderTests/QRCodeRendererTests.cs +++ b/QRCoderTests/QRCodeRendererTests.cs @@ -1,149 +1,148 @@ -#if SYSTEM_DRAWING -using Xunit; +#if SYSTEM_DRAWING +using System.Drawing; using QRCoder; -using Shouldly; -using QRCoderTests.Helpers.XUnitExtenstions; using QRCoderTests.Helpers; -using System.Drawing; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; + +namespace QRCoderTests; -namespace QRCoderTests + +public class QRCodeRendererTests { + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_standard_graphic() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new QRCode(data).GetGraphic(10); - public class QRCodeRendererTests + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_standard_graphic_hex() { - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_standard_graphic() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new QRCode(data).GetGraphic(10); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_standard_graphic_hex() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new QRCode(data).GetGraphic(10, "#000000", "#ffffff"); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); - } - - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_standard_graphic_without_quietzones() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new QRCode(data).GetGraphic(5, Color.Black, Color.White, false); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c401d45c01e636af3eb4b8ca6cd17d14"); - } - - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_with_transparent_logo_graphic() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_with_non_transparent_logo_graphic() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: (Bitmap)Bitmap.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("74808e52270bba92e7b821dbd067dfd2"); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_with_logo_and_with_transparent_border() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_with_logo_and_with_standard_border() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: logo, iconBorderWidth: 6); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("943ecd2a847a4d9509ca0266dbbadd7b"); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_create_qrcode_with_logo_and_with_custom_border() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6, iconBackgroundColor: Color.DarkGreen); - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("e60bdaafe807889ca322d47146fe8300"); - } - - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_instantate_qrcode_parameterless() - { - var svgCode = new QRCode(); - svgCode.ShouldNotBeNull(); - svgCode.ShouldBeOfType(); - } - - [Fact] - [Category("QRRenderer/QRCode")] - public void can_render_qrcode_from_helper() - { - //Create QR code - var bmp = QRCodeHelper.GetQRCode("This is a quick test! 123#?", 10, Color.Black, Color.White, QRCodeGenerator.ECCLevel.H); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); - } + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new QRCode(data).GetGraphic(10, "#000000", "#ffffff"); + + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_standard_graphic_without_quietzones() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new QRCode(data).GetGraphic(5, Color.Black, Color.White, false); + + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("c401d45c01e636af3eb4b8ca6cd17d14"); + } + + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_with_transparent_logo_graphic() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_with_non_transparent_logo_graphic() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: (Bitmap)Bitmap.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png")); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("74808e52270bba92e7b821dbd067dfd2"); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_with_logo_and_with_transparent_border() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("c99a82b43ce48ddae18a75862c476a9e"); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_with_logo_and_with_standard_border() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.White, icon: logo, iconBorderWidth: 6); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("943ecd2a847a4d9509ca0266dbbadd7b"); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_create_qrcode_with_logo_and_with_custom_border() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + var logo = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var bmp = new QRCode(data).GetGraphic(10, Color.Black, Color.Transparent, icon: logo, iconBorderWidth: 6, iconBackgroundColor: Color.DarkGreen); + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("e60bdaafe807889ca322d47146fe8300"); + } + + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_instantate_qrcode_parameterless() + { + var svgCode = new QRCode(); + svgCode.ShouldNotBeNull(); + svgCode.ShouldBeOfType(); + } + + [Fact] + [Category("QRRenderer/QRCode")] + public void can_render_qrcode_from_helper() + { + //Create QR code + var bmp = QRCodeHelper.GetQRCode("This is a quick test! 123#?", 10, Color.Black, Color.White, QRCodeGenerator.ECCLevel.H); + + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + } + } -#endif \ No newline at end of file +#endif diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj index ae537ef7..a31ba175 100644 --- a/QRCoderTests/QRCoderTests.csproj +++ b/QRCoderTests/QRCoderTests.csproj @@ -1,65 +1,64 @@ - - - net35;net452;netcoreapp1.1;netcoreapp2.0;net5.0;net5.0-windows;net6.0;net6.0-windows - true - true - $(DefineConstants);SYSTEM_DRAWING - $(DefineConstants);TEST_XAML - $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS - false - true - false - true - $(NoWarn);NU1903 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - Always - - - Always - - + + + net35;net452;netcoreapp1.1;netcoreapp2.0;net5.0;net5.0-windows;net6.0;net6.0-windows + true + true + $(DefineConstants);SYSTEM_DRAWING + $(DefineConstants);TEST_XAML + $(DefineConstants);NET5_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS + false + true + true + $(NoWarn);NU1903 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + Always + + + Always + + diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index bf8613dd..446c723e 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -1,646 +1,642 @@ -using QRCoder; -using QRCoderTests.Helpers.XUnitExtenstions; -using Shouldly; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +using QRCoder; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; using Xunit; using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel; -namespace QRCoderTests -{ - - public class QRGeneratorTests - { - [Fact] - [Category("QRGenerator/Antilog")] - public void validate_antilogtable() - { - var gen = new QRCodeGenerator(); +namespace QRCoderTests; - var checkString = string.Empty; - var gField = gen.GetType().GetField("galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); - gField.Length.ShouldBe(256); - for (int i = 0; i < gField.Length; i++) - { - checkString += i + "," + gField[i] + ",:"; - } - checkString.ShouldBe("0,1,:1,2,:2,4,:3,8,:4,16,:5,32,:6,64,:7,128,:8,29,:9,58,:10,116,:11,232,:12,205,:13,135,:14,19,:15,38,:16,76,:17,152,:18,45,:19,90,:20,180,:21,117,:22,234,:23,201,:24,143,:25,3,:26,6,:27,12,:28,24,:29,48,:30,96,:31,192,:32,157,:33,39,:34,78,:35,156,:36,37,:37,74,:38,148,:39,53,:40,106,:41,212,:42,181,:43,119,:44,238,:45,193,:46,159,:47,35,:48,70,:49,140,:50,5,:51,10,:52,20,:53,40,:54,80,:55,160,:56,93,:57,186,:58,105,:59,210,:60,185,:61,111,:62,222,:63,161,:64,95,:65,190,:66,97,:67,194,:68,153,:69,47,:70,94,:71,188,:72,101,:73,202,:74,137,:75,15,:76,30,:77,60,:78,120,:79,240,:80,253,:81,231,:82,211,:83,187,:84,107,:85,214,:86,177,:87,127,:88,254,:89,225,:90,223,:91,163,:92,91,:93,182,:94,113,:95,226,:96,217,:97,175,:98,67,:99,134,:100,17,:101,34,:102,68,:103,136,:104,13,:105,26,:106,52,:107,104,:108,208,:109,189,:110,103,:111,206,:112,129,:113,31,:114,62,:115,124,:116,248,:117,237,:118,199,:119,147,:120,59,:121,118,:122,236,:123,197,:124,151,:125,51,:126,102,:127,204,:128,133,:129,23,:130,46,:131,92,:132,184,:133,109,:134,218,:135,169,:136,79,:137,158,:138,33,:139,66,:140,132,:141,21,:142,42,:143,84,:144,168,:145,77,:146,154,:147,41,:148,82,:149,164,:150,85,:151,170,:152,73,:153,146,:154,57,:155,114,:156,228,:157,213,:158,183,:159,115,:160,230,:161,209,:162,191,:163,99,:164,198,:165,145,:166,63,:167,126,:168,252,:169,229,:170,215,:171,179,:172,123,:173,246,:174,241,:175,255,:176,227,:177,219,:178,171,:179,75,:180,150,:181,49,:182,98,:183,196,:184,149,:185,55,:186,110,:187,220,:188,165,:189,87,:190,174,:191,65,:192,130,:193,25,:194,50,:195,100,:196,200,:197,141,:198,7,:199,14,:200,28,:201,56,:202,112,:203,224,:204,221,:205,167,:206,83,:207,166,:208,81,:209,162,:210,89,:211,178,:212,121,:213,242,:214,249,:215,239,:216,195,:217,155,:218,43,:219,86,:220,172,:221,69,:222,138,:223,9,:224,18,:225,36,:226,72,:227,144,:228,61,:229,122,:230,244,:231,245,:232,247,:233,243,:234,251,:235,235,:236,203,:237,139,:238,11,:239,22,:240,44,:241,88,:242,176,:243,125,:244,250,:245,233,:246,207,:247,131,:248,27,:249,54,:250,108,:251,216,:252,173,:253,71,:254,142,:255,1,:"); - var gField2 = gen.GetType().GetField("galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); - gField2.Length.ShouldBe(256); - var checkString2 = string.Empty; - for (int i = 0; i < gField2.Length; i++) - { - checkString2 += i + "," + gField2[i] + ",:"; - } - checkString2.ShouldBe("0,0,:1,0,:2,1,:3,25,:4,2,:5,50,:6,26,:7,198,:8,3,:9,223,:10,51,:11,238,:12,27,:13,104,:14,199,:15,75,:16,4,:17,100,:18,224,:19,14,:20,52,:21,141,:22,239,:23,129,:24,28,:25,193,:26,105,:27,248,:28,200,:29,8,:30,76,:31,113,:32,5,:33,138,:34,101,:35,47,:36,225,:37,36,:38,15,:39,33,:40,53,:41,147,:42,142,:43,218,:44,240,:45,18,:46,130,:47,69,:48,29,:49,181,:50,194,:51,125,:52,106,:53,39,:54,249,:55,185,:56,201,:57,154,:58,9,:59,120,:60,77,:61,228,:62,114,:63,166,:64,6,:65,191,:66,139,:67,98,:68,102,:69,221,:70,48,:71,253,:72,226,:73,152,:74,37,:75,179,:76,16,:77,145,:78,34,:79,136,:80,54,:81,208,:82,148,:83,206,:84,143,:85,150,:86,219,:87,189,:88,241,:89,210,:90,19,:91,92,:92,131,:93,56,:94,70,:95,64,:96,30,:97,66,:98,182,:99,163,:100,195,:101,72,:102,126,:103,110,:104,107,:105,58,:106,40,:107,84,:108,250,:109,133,:110,186,:111,61,:112,202,:113,94,:114,155,:115,159,:116,10,:117,21,:118,121,:119,43,:120,78,:121,212,:122,229,:123,172,:124,115,:125,243,:126,167,:127,87,:128,7,:129,112,:130,192,:131,247,:132,140,:133,128,:134,99,:135,13,:136,103,:137,74,:138,222,:139,237,:140,49,:141,197,:142,254,:143,24,:144,227,:145,165,:146,153,:147,119,:148,38,:149,184,:150,180,:151,124,:152,17,:153,68,:154,146,:155,217,:156,35,:157,32,:158,137,:159,46,:160,55,:161,63,:162,209,:163,91,:164,149,:165,188,:166,207,:167,205,:168,144,:169,135,:170,151,:171,178,:172,220,:173,252,:174,190,:175,97,:176,242,:177,86,:178,211,:179,171,:180,20,:181,42,:182,93,:183,158,:184,132,:185,60,:186,57,:187,83,:188,71,:189,109,:190,65,:191,162,:192,31,:193,45,:194,67,:195,216,:196,183,:197,123,:198,164,:199,118,:200,196,:201,23,:202,73,:203,236,:204,127,:205,12,:206,111,:207,246,:208,108,:209,161,:210,59,:211,82,:212,41,:213,157,:214,85,:215,170,:216,251,:217,96,:218,134,:219,177,:220,187,:221,204,:222,62,:223,90,:224,203,:225,89,:226,95,:227,176,:228,156,:229,169,:230,160,:231,81,:232,11,:233,245,:234,22,:235,235,:236,122,:237,117,:238,44,:239,215,:240,79,:241,174,:242,213,:243,233,:244,230,:245,231,:246,173,:247,232,:248,116,:249,214,:250,244,:251,234,:252,168,:253,80,:254,88,:255,175,:"); - } +public class QRGeneratorTests +{ + [Fact] + [Category("QRGenerator/Antilog")] + public void validate_antilogtable() + { + var gen = new QRCodeGenerator(); -#if !NETFRAMEWORK // [Theory] is not supported in xunit < 2.0.0 - [Theory] - // version 1 numeric - [InlineData("1", "KWw84nkWZLMh5LqAJ/4s/4mW/08", 21)] - [InlineData("12", "+MdvzzZYQNF3d+6NuZGSmqmCmXY", 21)] - [InlineData("123", "meNWffAoC6ozzXEdDpEjixvBAME", 21)] - [InlineData("1234", "rOI2dmjilbVXsk4m2sJjAWybMto", 21)] - [InlineData("12345", "gVrbNyJNTkLCoXhLA1g1vGUlQvI", 21)] - [InlineData("123456", "TsdKS6PDgtq1b2stRT1C90DiGik", 21)] - [InlineData("1234567", "pbpVWmVQPjeRSPk/8GIlAbtPPlY", 21)] - [InlineData("12345678", "ng29QoMxhqMsygeU7t2Ic9RB2hk", 21)] - [InlineData("123456789", "Xb/EHaUUUU+22a4Hm/2Sr+O1zv0", 21)] - [InlineData("1234567890", "X0kmbmnqpAFjTuS0SQAEkphAaok", 21)] - [InlineData("1234567890123456", "afOstf4rTgaLUaHL/Vb23vzjQFM", 21)] - [InlineData("12345678901234567", "S0BOgmRblr9Bb6Lpkf62WfYIj58", 21)] - // version 2 numeric - [InlineData("123456789012345678", "qs5j+bBK3fdRgoQg1N00vUF7f0g", 25)] - [InlineData("1234567890123456789012345678901234", "mu6wUZp+uXqXGyYFduQZt38Jbu0", 25)] - // version 3 numeric - [InlineData("12345678901234567890123456789012345", "AiWiTB6xreLc514aHw4StDsomvk", 29)] - [InlineData("1234567890123456789012345678901234567890123456789012345678", "WNLD0ved5WdysFG1uqNBBV7ItKI", 29)] - // version 4 numeric - [InlineData("12345678901234567890123456789012345678901234567890123456789", "cV6Rijj6q3f/dUlDVOZD3DafrMM", 33)] - - // version 1 alphanumeric - [InlineData("A", "YUpoycThbE3FwkkHaO6GYqe9V+c", 21)] - [InlineData("AB", "UnUHZDgLdnYIy0iN31sguw2qbh8", 21)] - [InlineData("ABC", "GVB3xcSMAawwOZlq0hiF9hqVldg", 21)] - [InlineData("ABCD", "jATOwpwGVWpou3WtKiq4DX4jWkk", 21)] - [InlineData("ABCDE", "m/LrK4iP22OW9RmC2r2dnDFd4wE", 21)] - [InlineData("ABCDEF", "p8acVHkm3z751oh5yK4mBBRMUuE", 21)] - [InlineData("ABCDEFG", "md1jFcZSqDmQ2KeFTwKJVFrfZko", 21)] - [InlineData("ABCDEFGH", "XvL+fpHNqQQ2FHUCXraQw77DGns", 21)] - [InlineData("ABCDEFGHI", "k+DTXI3yht473k9lvYLMdHf0V/0", 21)] - [InlineData("ABCDEFGHIJ", "f9uful+85iSlJVJAFc5zEk04eMc", 21)] - // version 2 alphanumeric - [InlineData("ABCDEFGHIJK", "qiXut4Jz2zX8Tl9DSXxIo+bqjZY", 25)] - [InlineData("ABCDEFGHIJKL", "wSpjZmpo9CEjlxlYF18xEa6BMYM", 25)] - [InlineData("ABCDEFGHIJKLM", "utCYtAtJp+GdKS6y6A7jQuES6kA", 25)] - [InlineData("ABCDEFGHIJKLMN", "jhzNewJNcC875mlYI31BkVgx0G0", 25)] - [InlineData("ABCDEFGHIJKLMNO", "eWCSdyn3EH3uFDig1a0NUYZFlO0", 25)] - [InlineData("ABCDEFGHIJKLMNOP", "glV9FE+UQPDkplgOXFhk3Ll29pI", 25)] - [InlineData("ABCDEFGHIJKLMNOPQ", "Crlq92Pqiw8X9EIG6KFCvStNQuI", 25)] - [InlineData("ABCDEFGHIJKLMNOPQR", "lIG8/YBsD0uK+Dop6QfvD7IFdAY", 25)] - [InlineData("ABCDEFGHIJKLMNOPQRS", "rsDPSYotVP65kLAxP3fzbGqt6wc", 25)] - [InlineData("ABCDEFGHIJKLMNOPQRST", "yOb1jCKlj3wqdczHEPRdWxafvNU", 25)] - // version 3 alphanumeric - [InlineData("ABCDEFGHIJKLMNOPQRSTU", "U4YNhfjgJbprsTurHs4E7Mi2sS8", 29)] - [InlineData("ABCDEFGHIJKLMNOPQRSTUV", "s9WCvPzYhXrwzNoVbDocQtse1w8", 29)] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVW", "6vZ9rDMy1GUVEcnL2ErJFOxqWI0", 29)] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWX", "C9LlmjRV+TPDkR03MlgDvo/DP+U", 29)] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", "+EtALGm0mrDrnZVW54WdXG612P0", 29)] - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ789012345", "3nFUvZ/Aa2wUdAj1zlMmSu9x4kU", 29)] - // version 4 alphanumeric - [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ7890123456", "9K6EinxynS2KRum46iQsVoPgM7k", 33)] - - // version 1 binary - [InlineData("a", "zd6kApf0BQSE5W8fhCijkz6wzKA", 21)] - [InlineData("ab", "mXAUC/dqcqqj6SzC+Us6NiYzzCM", 21)] - [InlineData("abc", "6Y3HGyOFxhZUYINks/hzE2DjulM", 21)] - [InlineData("abcd", "ssvlMmEub85t1d0R/aZG+Qgpa+0", 21)] - [InlineData("abcde", "At93DjDyAtIkTCpOwD3p/lSqFz4", 21)] - [InlineData("abcdef", "Q8BU1lCJJA/UesKGvszQTptskSk", 21)] - [InlineData("abcdefg", "2AceZvcjIpEBCh5FIc1esXsaEY4", 21)] - // version 2 binary - [InlineData("abcdefgh", "kl8bx15B4ApauURa0nlD71NPk+I", 25)] - [InlineData("abcdefghi", "v9rI0/2a8nYxM9MerzxSCwT6EKs", 25)] - [InlineData("abcdefghij", "yE59+LfDLQCt2VXCuhnz9aFIoMk", 25)] - [InlineData("abcdefghijk", "nVne+lyjPV5XDMKqa0+oNfxZTgI", 25)] - [InlineData("abcdefghijkl", "QUeDHmjQDyHvbe5r8tViXxBcHv0", 25)] - [InlineData("abcdefghijklm", "WtN1tTti8hV4vvH5vX6obIPdjpM", 25)] - [InlineData("abcdefghijklmn", "AT5SPNUPL3wG0r4XXPBzSAK2sIE", 25)] - // version 3 binary - [InlineData("abcdefghijklmno", "N04AFOJlXQRjeXijWoy4rsBNZGg", 29)] - [InlineData("abcdefghijklmnop", "a4tgwBApGX0+P4yiwR/wUtLAxQA", 29)] - [InlineData("abcdefghijklmnopq", "MFLd+exCRrUZkqfw5UTqY2QZ1n0", 29)] - [InlineData("abcdefghijklmnopqr", "aSYOJXfFAjxrtBWnBQqHWrC8Zv0", 29)] - [InlineData("abcdefghijklmnopqrs", "K9Uic6+NO2rPy/Hfo4fEhXkUw2Q", 29)] - [InlineData("abcdefghijklmnopqrst", "eKVJvIH8J1waEb3UHRdXYAWLezc", 29)] - [InlineData("abcdefghijklmnopqrstu", "ylmFLWV1grM2MoTFpdngo05fdyI", 29)] - [InlineData("abcdefghijklmnopqrstuv", "Z0IETxnf8x+pTU2nuj1hxg2G/pQ", 29)] - [InlineData("abcdefghijklmnopqrstuvw", "oHzGRWtkI+a30AF5JILT6HON7Zc", 29)] - [InlineData("abcdefghijklmnopqrstuvwx", "NRIoT6rGd3HWrBq4JhBWvbwYp9g", 29)] - // version 4 binary - [InlineData("abcdefghijklmnopqrstuvwxy", "RMSyMOBpdBYphJPkXR/xA/ekPoo", 33)] - [InlineData("abcdefghijklmnopqrstuvwxyz", "BAMiY251UapecfI+v2C3cX2EBH4", 33)] - // version 5 binary - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghi", "yV9Cd3xiW2HRzSIMq3eLTIrdqVQ", 37)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghij", "mV/R+gMAwN+lO8ByXhU5IyZp39Y", 37)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijk", "sIb5hBRamy+MIgaFakCCGnDM9yU", 37)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijkl", "2/PZLsxe4c/R/tStrn9pcB8EUOQ", 37)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklm", "KFReyVpr4rq5c+ELZBt/ZuhQkYM", 37)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmn", "IwlWmCnXp0FSr+WUp/igMuQKHQo", 37)] - // version 7 binary - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg", "bisFBjhANRxoF9JDCBSODvsSKqk", 45)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh", "MwRnhkqr5CM17xtcQycytd+d+Fs", 45)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi", "PFlhVI0La4/qOweduCP2WfedoCQ", 45)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij", "ZwMdK51id9A99IxefE01o5ZtkN4", 45)] - [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", "HM6MMwWDmJ0PTLLBWzIo7Q0YvmA", 45)] - public void can_encode_various_strings_ecc_h(string input, string expectedHash, int expectedSize) + var checkString = string.Empty; + var gField = gen.GetType().GetField("_galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + gField.Length.ShouldBe(256); + for (int i = 0; i < gField.Length; i++) { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode(input, ECCLevel.H); - (qrData.ModuleMatrix.Count - 8).ShouldBe(expectedSize); // exclude padding - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - var hash = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(result)); - var hashString = Convert.ToBase64String(hash); - hashString.TrimEnd('=').ShouldBe(expectedHash); + checkString += i + "," + gField[i] + ",:"; } + checkString.ShouldBe("0,1,:1,2,:2,4,:3,8,:4,16,:5,32,:6,64,:7,128,:8,29,:9,58,:10,116,:11,232,:12,205,:13,135,:14,19,:15,38,:16,76,:17,152,:18,45,:19,90,:20,180,:21,117,:22,234,:23,201,:24,143,:25,3,:26,6,:27,12,:28,24,:29,48,:30,96,:31,192,:32,157,:33,39,:34,78,:35,156,:36,37,:37,74,:38,148,:39,53,:40,106,:41,212,:42,181,:43,119,:44,238,:45,193,:46,159,:47,35,:48,70,:49,140,:50,5,:51,10,:52,20,:53,40,:54,80,:55,160,:56,93,:57,186,:58,105,:59,210,:60,185,:61,111,:62,222,:63,161,:64,95,:65,190,:66,97,:67,194,:68,153,:69,47,:70,94,:71,188,:72,101,:73,202,:74,137,:75,15,:76,30,:77,60,:78,120,:79,240,:80,253,:81,231,:82,211,:83,187,:84,107,:85,214,:86,177,:87,127,:88,254,:89,225,:90,223,:91,163,:92,91,:93,182,:94,113,:95,226,:96,217,:97,175,:98,67,:99,134,:100,17,:101,34,:102,68,:103,136,:104,13,:105,26,:106,52,:107,104,:108,208,:109,189,:110,103,:111,206,:112,129,:113,31,:114,62,:115,124,:116,248,:117,237,:118,199,:119,147,:120,59,:121,118,:122,236,:123,197,:124,151,:125,51,:126,102,:127,204,:128,133,:129,23,:130,46,:131,92,:132,184,:133,109,:134,218,:135,169,:136,79,:137,158,:138,33,:139,66,:140,132,:141,21,:142,42,:143,84,:144,168,:145,77,:146,154,:147,41,:148,82,:149,164,:150,85,:151,170,:152,73,:153,146,:154,57,:155,114,:156,228,:157,213,:158,183,:159,115,:160,230,:161,209,:162,191,:163,99,:164,198,:165,145,:166,63,:167,126,:168,252,:169,229,:170,215,:171,179,:172,123,:173,246,:174,241,:175,255,:176,227,:177,219,:178,171,:179,75,:180,150,:181,49,:182,98,:183,196,:184,149,:185,55,:186,110,:187,220,:188,165,:189,87,:190,174,:191,65,:192,130,:193,25,:194,50,:195,100,:196,200,:197,141,:198,7,:199,14,:200,28,:201,56,:202,112,:203,224,:204,221,:205,167,:206,83,:207,166,:208,81,:209,162,:210,89,:211,178,:212,121,:213,242,:214,249,:215,239,:216,195,:217,155,:218,43,:219,86,:220,172,:221,69,:222,138,:223,9,:224,18,:225,36,:226,72,:227,144,:228,61,:229,122,:230,244,:231,245,:232,247,:233,243,:234,251,:235,235,:236,203,:237,139,:238,11,:239,22,:240,44,:241,88,:242,176,:243,125,:244,250,:245,233,:246,207,:247,131,:248,27,:249,54,:250,108,:251,216,:252,173,:253,71,:254,142,:255,1,:"); - [Theory] - // Version 1 - [InlineData(17, ECCLevel.L, "iOaoY7YsHAYGNRn+Tpnt74IQoVw=", 21)] - [InlineData(14, ECCLevel.M, "JV2XYoq8nt/lWipVkwvvSbNvFVQ=", 21)] - [InlineData(11, ECCLevel.Q, "44vd54SCPFEevWN9PKC5swEpVmU=", 21)] - [InlineData(7, ECCLevel.H, "FvR2FAU+4sltHMS969/Y1FAHZRA=", 21)] - // Version 2 - [InlineData(32, ECCLevel.L, "vM4eIrKbner3NxjRznd6kZLbyck=", 25)] - [InlineData(26, ECCLevel.M, "mesaTID5N92ar2fyElorp7zcSVg=", 25)] - [InlineData(20, ECCLevel.Q, "mg1Z+VPVuxMoGwvgRzJrW4NHehA=", 25)] - [InlineData(14, ECCLevel.H, "6T0I6Z9AmN9yNIvan82NQqAMATc=", 25)] - // Version 3 - [InlineData(53, ECCLevel.L, "O8Wkal/iDmCnENBubqR0HXOo/RY=", 29)] - [InlineData(42, ECCLevel.M, "NCjzwIm3l5urwU4EcFGK5DD1y9U=", 29)] - [InlineData(32, ECCLevel.Q, "kR+4FNybKyAiGDLPDnzIslvjypQ=", 29)] - [InlineData(24, ECCLevel.H, "ZamyoGRJG7mGMY6nzz8r3+q0z18=", 29)] - // Version 4 - [InlineData(78, ECCLevel.L, "P3Dx7gDjD2L94wPyL23AO/z5+Yk=", 33)] - [InlineData(62, ECCLevel.M, "wDUcQmVTCcTx6sStDlPG4Wsn5FU=", 33)] - [InlineData(46, ECCLevel.Q, "3mYP2cHqQysy93UC4NGUnNwfj10=", 33)] - [InlineData(34, ECCLevel.H, "5ovh+NFiGh6soAuNNTWqxenM8cw=", 33)] - // Version 5 - [InlineData(106, ECCLevel.L, "0vZswNhwdcCcj2GpwucfkcnlG/M=", 37)] - [InlineData(84, ECCLevel.M, "AWlV4NsbRtkmH2/vfBxTahIiG7U=", 37)] - [InlineData(60, ECCLevel.Q, "8w35kxFqvcMajba9IvRhjbOn0Js=", 37)] - [InlineData(44, ECCLevel.H, "+d0gLH3v9FG+w/hhv+zDm2Y3IVw=", 37)] - // Version 6 - [InlineData(134, ECCLevel.L, "CNyyNDIylrMi97DwuNh6JAgHlw8=", 41)] - [InlineData(106, ECCLevel.M, "z4LUkv75O26FLaVo823TMLv9Owg=", 41)] - [InlineData(74, ECCLevel.Q, "NgCKIxbeuSt24C9M067nDGopKgU=", 41)] - [InlineData(58, ECCLevel.H, "Bzp923oooHYWoQfETFENmb5wup0=", 41)] - // Version 7 - [InlineData(154, ECCLevel.L, "ftMeEbWj6D0lyOBVsAnTCq0UV0s=", 45)] - [InlineData(122, ECCLevel.M, "zif9uHXnPgo+OeIN95xU3iqcexk=", 45)] - [InlineData(86, ECCLevel.Q, "wApf2GzMYIQlYw4ws3k6Wi1DqMU=", 45)] - [InlineData(64, ECCLevel.H, "i8llCv2L4dwlW5E8+mswsAa+Zo4=", 45)] - // Version 8 - [InlineData(192, ECCLevel.L, "DSph/W1Nq2VAKFxgRq0VeqTP54g=", 49)] - [InlineData(152, ECCLevel.M, "j06phyT/k6pXqf935BuaMxUjckk=", 49)] - [InlineData(108, ECCLevel.Q, "RufVav4xUUuL/K5ELnH3/qUrEf8=", 49)] - [InlineData(84, ECCLevel.H, "oG538pE6ac81I2of3LzIHQ6+Dxg=", 49)] - // Version 9 - [InlineData(230, ECCLevel.L, "LBs30yL9Rec1qFdPwKz4nBrDraY=", 53)] - [InlineData(180, ECCLevel.M, "c0DN8hFoX6SEkVKr/yVA79SZE4g=", 53)] - [InlineData(130, ECCLevel.Q, "XdZ3zNyz14Sq0fv9KjonZK7ok04=", 53)] - [InlineData(98, ECCLevel.H, "9NE1egSXCdGY8AiY4LhHM6sO/jA=", 53)] - // Version 10 - [InlineData(271, ECCLevel.L, "gyox9Nk2DCPzVeL2E1V/P5XsuNY=", 57)] - [InlineData(213, ECCLevel.M, "iqS7CNYuwZpw47/SnM8JAcWkhCE=", 57)] - [InlineData(151, ECCLevel.Q, "vVdh2R+yWmSeDc7iCKonTTcs4ok=", 57)] - [InlineData(119, ECCLevel.H, "n79CbR/JZZC30sDIDjdFAgurzR4=", 57)] - // Version 11 - [InlineData(321, ECCLevel.L, "R77LLVhO+/YE8WKmn3CV9f/I9ZY=", 61)] - [InlineData(251, ECCLevel.M, "l/IFWD6Pkm1TZHFc4ZuFLWDrfdc=", 61)] - [InlineData(177, ECCLevel.Q, "SxVElF8qBWe0oXXGn57CoI6iglo=", 61)] - [InlineData(137, ECCLevel.H, "GrHJ2EiDMJ/cXpjcITvypJZZGrY=", 61)] - // Version 12 - [InlineData(367, ECCLevel.L, "rCv4hIrv0obcHALDSvzN/5zwCfg=", 65)] - [InlineData(287, ECCLevel.M, "mBC3lYhpNuCa2TbD/h+F6gFH8f4=", 65)] - [InlineData(203, ECCLevel.Q, "2Gpr+HihG8dDshcf96n2lNopsiM=", 65)] - [InlineData(155, ECCLevel.H, "eMjatzihLLJH1KZ56GAmaXyf/os=", 65)] - // Version 13 - [InlineData(425, ECCLevel.L, "mmPOWpjyRZWVC+JRJFDpufEqbUk=", 69)] - [InlineData(331, ECCLevel.M, "nujBdyCZyO4HoHL4uLYIucd/MA4=", 69)] - [InlineData(241, ECCLevel.Q, "IT+VAECAcwuZqJdQft5fWo/UTMs=", 69)] - [InlineData(177, ECCLevel.H, "7JczSXSWYg5XXPhdqLx4Lb411lU=", 69)] - // Version 14 - [InlineData(458, ECCLevel.L, "a/hMcMmEajVBC3kj8ILzRdGR4t0=", 73)] - [InlineData(362, ECCLevel.M, "M7D1Cm0FeVbNeiZd+yUPp/8lDfU=", 73)] - [InlineData(258, ECCLevel.Q, "KAnpA9g4esUdsXHLBpIVvbJ/Dsw=", 73)] - [InlineData(194, ECCLevel.H, "gs48PFtIXdBTsNB5CIDK4IopcMU=", 73)] - // Version 15 - [InlineData(520, ECCLevel.L, "L1/Q9lMcmGeNwY4RbBTfFKk2CAQ=", 77)] - [InlineData(412, ECCLevel.M, "7Z4o8qbi+HXAh5wSBlg/KO8VWl8=", 77)] - [InlineData(292, ECCLevel.Q, "v52Vt1lJpaEOWkfIsmRMeF2VkZ4=", 77)] - [InlineData(220, ECCLevel.H, "0c8GsO3CIWhcJYcQDE92+l+w7rQ=", 77)] - // Version 16 - [InlineData(586, ECCLevel.L, "M66FY6iMOawr2JoEInl0KBKQ1nI=", 81)] - [InlineData(450, ECCLevel.M, "5u8qfYfrBGxzyesU/xepVqwaWWw=", 81)] - [InlineData(322, ECCLevel.Q, "QnNtWtlQDmt6cu535YqceOZAyVY=", 81)] - [InlineData(250, ECCLevel.H, "w2O4eKcEL43ibEH/dDzbNqGDFaM=", 81)] - // Version 17 - [InlineData(644, ECCLevel.L, "7uG763m0mGPJdY9nwquzdiR4Yu8=", 85)] - [InlineData(504, ECCLevel.M, "FJuxPTwkgTFHIiFOKfdMMjins2Y=", 85)] - [InlineData(364, ECCLevel.Q, "JPuf2oD8xEeJSY/bhIO7VCbDxfI=", 85)] - [InlineData(280, ECCLevel.H, "iepqbSMD9KO0jdaBHdDD3CN/ELA=", 85)] - // Version 18 - [InlineData(718, ECCLevel.L, "tg9fRcelrfpz1muMC3bp9Rd+d+Q=", 89)] - [InlineData(560, ECCLevel.M, "ZiJ3ALPxKefddqcbFsaLVtaqu4M=", 89)] - [InlineData(394, ECCLevel.Q, "TetEWsqYm2DnePzsBN2n2TZI1qw=", 89)] - [InlineData(310, ECCLevel.H, "g3SqiNtegQKKWz0fphMJNbMnauI=", 89)] - // Version 19 - [InlineData(792, ECCLevel.L, "G3wlYoJhuxgOMAhwlBSlenIPzQE=", 93)] - [InlineData(624, ECCLevel.M, "zlQfupN9mxSqabm5IH0Au5UltHA=", 93)] - [InlineData(442, ECCLevel.Q, "ZTqF6EL1yCtaRxB2/fwuOUQVyDo=", 93)] - [InlineData(338, ECCLevel.H, "R2zVkSuv/xkSn5tz4RW/8z1Tu+U=", 93)] - // Version 20 - [InlineData(858, ECCLevel.L, "picn/dsww2hy+2gWQCFJfWRryvM=", 97)] - [InlineData(666, ECCLevel.M, "65jjl86TbBbuzJk6n42DYMGWVtI=", 97)] - [InlineData(482, ECCLevel.Q, "78k1kZhh228wPC1HNLuHR2E2rXI=", 97)] - [InlineData(382, ECCLevel.H, "cjCcbhd7GuNpePeTVeEXS11ZrXk=", 97)] - // Version 21 - [InlineData(929, ECCLevel.L, "88t3Y6RQA+g+6r8mp9RUuMkZGP0=", 101)] - [InlineData(711, ECCLevel.M, "Xlq1Xfk881mzrCO+Iu8kK8brGBg=", 101)] - [InlineData(509, ECCLevel.Q, "KkN2utAu40CZuKCUN0jLWJ+Vd6o=", 101)] - [InlineData(403, ECCLevel.H, "L44GKy1dAMakEhA4UO3rZscrjys=", 101)] - // Version 22 - [InlineData(1003, ECCLevel.L, "QN5/o8D8VuuH8hDP+DIItgKwPb0=", 105)] - [InlineData(779, ECCLevel.M, "K6ss7doERSJbYd2EAWWgB8q/tjQ=", 105)] - [InlineData(565, ECCLevel.Q, "uO4I8RZ1QBgOdlV36LGFCHeNOzk=", 105)] - [InlineData(439, ECCLevel.H, "wdAxJat/xdz5D/A53Twe7LijnHg=", 105)] - // Version 23 - [InlineData(1091, ECCLevel.L, "ZIkAmmyIUotcDSfA4APqqBrb1WY=", 109)] - [InlineData(857, ECCLevel.M, "7h/MVR1ognfK4SVDRmfgyy7UJVA=", 109)] - [InlineData(611, ECCLevel.Q, "NYW8AFfV48ojWSAiZjnMYR98o78=", 109)] - [InlineData(461, ECCLevel.H, "7rQgH3LE/YyOjpqH3XficevFSaU=", 109)] - // Version 24 - [InlineData(1171, ECCLevel.L, "0pNvS9HfwsAOLG6/U1PXKMyoc6I=", 113)] - [InlineData(911, ECCLevel.M, "555aDmkhsRGE2li1z81j6mVMh7s=", 113)] - [InlineData(661, ECCLevel.Q, "UJQmA6QoWlN3r8BEF+zIFvWsoJ4=", 113)] - [InlineData(511, ECCLevel.H, "emTvRmvoSWFRHLljWXOzvUjpLX0=", 113)] - // Version 25 - [InlineData(1273, ECCLevel.L, "vf+HWAPl5vJXIKHrZCPHjAInuo4=", 117)] - [InlineData(997, ECCLevel.M, "v+H4HXOL3tO5/QIsK1IYPGu+zA0=", 117)] - [InlineData(715, ECCLevel.Q, "rc+LXSs8ILA84TabFJ5b45gX0n8=", 117)] - [InlineData(535, ECCLevel.H, "ZidXJ+kT23SwS9+xbLZ755ATt4g=", 117)] - // Version 26 - [InlineData(1367, ECCLevel.L, "QPNW+iYKQdJClPaZsuTUju6dsQE=", 121)] - [InlineData(1059, ECCLevel.M, "NLTDTTGBmvzR6TOUSxTF/EY4oLI=", 121)] - [InlineData(751, ECCLevel.Q, "MoOh9EA/7kESiuzy6YHJWcjujMM=", 121)] - [InlineData(593, ECCLevel.H, "16reau4y5ukinTo0YfM1ToP+/nE=", 121)] - // Version 27 - [InlineData(1465, ECCLevel.L, "NeSmv01ZOFMeRCxIu6AshMdonOM=", 125)] - [InlineData(1125, ECCLevel.M, "4xKzxZ7KvzpFAlFmEXYXDHuKh2A=", 125)] - [InlineData(805, ECCLevel.Q, "LWbwDKIZKwqnfJQncZM+SXJ2qmg=", 125)] - [InlineData(625, ECCLevel.H, "ks+3sSJU4VFavu6ILO7zFVQZVqc=", 125)] - // Version 28 - [InlineData(1528, ECCLevel.L, "h64k62PMqcq+hpKSUEFbfNwOlxY=", 129)] - [InlineData(1190, ECCLevel.M, "H8mNI2+l1xp6LAJKc54t6BcTgXE=", 129)] - [InlineData(868, ECCLevel.Q, "s+IrQoE960X/7R6mFQyw6w3d0C4=", 129)] - [InlineData(658, ECCLevel.H, "ugGZT7++E0kKqEi1lOwpgGIuilU=", 129)] - // Version 29 - [InlineData(1628, ECCLevel.L, "ZJ2nILLA7z8tcMK/NUayp0gd+Ws=", 133)] - [InlineData(1264, ECCLevel.M, "oVnYNf73ATNLaLNV+Kcsk/kkJ5g=", 133)] - [InlineData(908, ECCLevel.Q, "G/4ZQB/lgaaP8cfzy6tyQkA4LCk=", 133)] - [InlineData(698, ECCLevel.H, "snR+O40DDNFn6tGio9X3Qx7Yj6I=", 133)] - // Version 30 - [InlineData(1732, ECCLevel.L, "QnOSXQy6rgnX3mOEFilBMYagxd4=", 137)] - [InlineData(1370, ECCLevel.M, "JV+jGwxg94rTaQB1pYEbyznyBg4=", 137)] - [InlineData(982, ECCLevel.Q, "nI2Xd3XP9ozg+YUns5VN1JvyLqg=", 137)] - [InlineData(742, ECCLevel.H, "YZKS3IH1kVNvndDJHxQGdRk6WNY=", 137)] - // Version 31 - [InlineData(1840, ECCLevel.L, "jjvNCmxgZzQnl0gtfx8AXGDzr64=", 141)] - [InlineData(1452, ECCLevel.M, "8P5rLwbI3czsK65jSnWYHK540ps=", 141)] - [InlineData(1030, ECCLevel.Q, "Z/+XPRRYe4AL90fdeD2pd9BtEJo=", 141)] - [InlineData(790, ECCLevel.H, "cauLAUrqzrUlDce6QvLc0QDVb8o=", 141)] - // Version 32 - [InlineData(1952, ECCLevel.L, "heo6yDq1jE3lrJBGQRWFq6Yxw4Y=", 145)] - [InlineData(1538, ECCLevel.M, "Owl+uFMlPWFUDa14YKPHl6Iz2rw=", 145)] - [InlineData(1112, ECCLevel.Q, "iH25k4mUj1LZFfH9RlRU7w1mvgU=", 145)] - [InlineData(842, ECCLevel.H, "u6fkuEiZbHyROePmzV22/fi4BUQ=", 145)] - // Version 33 - [InlineData(2068, ECCLevel.L, "d9ATU4zloW1TWbDyFUQRnstXX80=", 149)] - [InlineData(1628, ECCLevel.M, "jJxIdcbC4JK6ilHF3tCLUN2z12Q=", 149)] - [InlineData(1168, ECCLevel.Q, "ODr9TeLZGwhJOkKiFTNfzxGeD5E=", 149)] - [InlineData(898, ECCLevel.H, "FxTOLRMB5YQw1y4Z2mxTOv+5p3g=", 149)] - // Version 34 - [InlineData(2188, ECCLevel.L, "hOS3RBFGQAIBqVxI1dJD3DMbPiM=", 153)] - [InlineData(1722, ECCLevel.M, "6ljLKSekU2f3IZSVYt2SmkCwxcQ=", 153)] - [InlineData(1228, ECCLevel.Q, "CMPPw3e2cUtxuPp6vyW4FLgltdg=", 153)] - [InlineData(958, ECCLevel.H, "ZHTja2LTLTgrd9Ha0awn5HiB9Pk=", 153)] - // Version 35 - [InlineData(2303, ECCLevel.L, "pmSX3DjwSMpia+KAM+MYi+jAyN4=", 157)] - [InlineData(1809, ECCLevel.M, "Uk6fudf3ij96QeYbDKMqyuC7ccY=", 157)] - [InlineData(1283, ECCLevel.Q, "Sss6YeZZjl/eA019B0vHOMAivG0=", 157)] - [InlineData(983, ECCLevel.H, "NXJCn5THFStoGeJdQ3gqVfpSQic=", 157)] - // Version 36 - [InlineData(2431, ECCLevel.L, "3aSKsgEZlsJ6PisZN9f55NPecHg=", 161)] - [InlineData(1911, ECCLevel.M, "OmKYn++0akH80oMbs/CAUCoHFsY=", 161)] - [InlineData(1351, ECCLevel.Q, "WWyI18sPU1e4z7/D6/tND0oJhps=", 161)] - [InlineData(1051, ECCLevel.H, "m38wtnevvqtYxDfS4dM7xDMaI/M=", 161)] - // Version 37 - [InlineData(2563, ECCLevel.L, "YV6EWRw9/HGfVO0COUoQ94uGRRg=", 165)] - [InlineData(1989, ECCLevel.M, "/KjlKPlECYqj/pX/Dz3wl9lLKsE=", 165)] - [InlineData(1423, ECCLevel.Q, "mI+9aaDdXQHxir65csXwE487Zvg=", 165)] - [InlineData(1093, ECCLevel.H, "f+oGwtL745K4S3x25qNBY8XCGyA=", 165)] - // Version 38 - [InlineData(2699, ECCLevel.L, "jFAE8Cw77UN/uf8rndzymM7Idwc=", 169)] - [InlineData(2099, ECCLevel.M, "roNsVHUcMngjE4/GyJop++9eeRs=", 169)] - [InlineData(1499, ECCLevel.Q, "G2/TnwE1HSnoTytk0mr4552oIos=", 169)] - [InlineData(1139, ECCLevel.H, "fou4BP/tpD9oxj7KeXyzZ6Fo+xc=", 169)] - // Version 39 - [InlineData(2809, ECCLevel.L, "IZ2L6FrpTQdjqls020r8YEWiFU8=", 173)] - [InlineData(2213, ECCLevel.M, "iybO3rmtGmgVRcOYkDnfrH7Scpo=", 173)] - [InlineData(1579, ECCLevel.Q, "O8jQuoPVybJuJy9ULv/xfJKA29Y=", 173)] - [InlineData(1219, ECCLevel.H, "FD2FN7bSh8ispau30YQRyNN5LL0=", 173)] - // Version 40 - [InlineData(2953, ECCLevel.L, "a+80y4pPAhCywuraFrnMSTFRRmo=", 177)] - [InlineData(2331, ECCLevel.M, "jLKuYZ7beIij+5j9Ko6GRxZVzaA=", 177)] - [InlineData(1663, ECCLevel.Q, "G1vhiI8anCkTOgeQPQAVH3xcSk8=", 177)] - [InlineData(1273, ECCLevel.H, "A0HAgMWn4TvnFfSnnEhQ0cVXcNU=", 177)] - public void can_encode_various_strings_various_ecc(int inputChars, ECCLevel eccLevel, string expectedHash, int expectedSize) + var gField2 = gen.GetType().GetField("_galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + gField2.Length.ShouldBe(256); + var checkString2 = string.Empty; + for (int i = 0; i < gField2.Length; i++) { - var input = new string('a', inputChars); - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode(input, eccLevel); - (qrData.ModuleMatrix.Count - 8).ShouldBe(expectedSize); // exclude padding - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - var hash = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(result)); - var hashString = Convert.ToBase64String(hash); - hashString.ShouldBe(expectedHash); + checkString2 += i + "," + gField2[i] + ",:"; } -#endif + checkString2.ShouldBe("0,0,:1,0,:2,1,:3,25,:4,2,:5,50,:6,26,:7,198,:8,3,:9,223,:10,51,:11,238,:12,27,:13,104,:14,199,:15,75,:16,4,:17,100,:18,224,:19,14,:20,52,:21,141,:22,239,:23,129,:24,28,:25,193,:26,105,:27,248,:28,200,:29,8,:30,76,:31,113,:32,5,:33,138,:34,101,:35,47,:36,225,:37,36,:38,15,:39,33,:40,53,:41,147,:42,142,:43,218,:44,240,:45,18,:46,130,:47,69,:48,29,:49,181,:50,194,:51,125,:52,106,:53,39,:54,249,:55,185,:56,201,:57,154,:58,9,:59,120,:60,77,:61,228,:62,114,:63,166,:64,6,:65,191,:66,139,:67,98,:68,102,:69,221,:70,48,:71,253,:72,226,:73,152,:74,37,:75,179,:76,16,:77,145,:78,34,:79,136,:80,54,:81,208,:82,148,:83,206,:84,143,:85,150,:86,219,:87,189,:88,241,:89,210,:90,19,:91,92,:92,131,:93,56,:94,70,:95,64,:96,30,:97,66,:98,182,:99,163,:100,195,:101,72,:102,126,:103,110,:104,107,:105,58,:106,40,:107,84,:108,250,:109,133,:110,186,:111,61,:112,202,:113,94,:114,155,:115,159,:116,10,:117,21,:118,121,:119,43,:120,78,:121,212,:122,229,:123,172,:124,115,:125,243,:126,167,:127,87,:128,7,:129,112,:130,192,:131,247,:132,140,:133,128,:134,99,:135,13,:136,103,:137,74,:138,222,:139,237,:140,49,:141,197,:142,254,:143,24,:144,227,:145,165,:146,153,:147,119,:148,38,:149,184,:150,180,:151,124,:152,17,:153,68,:154,146,:155,217,:156,35,:157,32,:158,137,:159,46,:160,55,:161,63,:162,209,:163,91,:164,149,:165,188,:166,207,:167,205,:168,144,:169,135,:170,151,:171,178,:172,220,:173,252,:174,190,:175,97,:176,242,:177,86,:178,211,:179,171,:180,20,:181,42,:182,93,:183,158,:184,132,:185,60,:186,57,:187,83,:188,71,:189,109,:190,65,:191,162,:192,31,:193,45,:194,67,:195,216,:196,183,:197,123,:198,164,:199,118,:200,196,:201,23,:202,73,:203,236,:204,127,:205,12,:206,111,:207,246,:208,108,:209,161,:210,59,:211,82,:212,41,:213,157,:214,85,:215,170,:216,251,:217,96,:218,134,:219,177,:220,187,:221,204,:222,62,:223,90,:224,203,:225,89,:226,95,:227,176,:228,156,:229,169,:230,160,:231,81,:232,11,:233,245,:234,22,:235,235,:236,122,:237,117,:238,44,:239,215,:240,79,:241,174,:242,213,:243,233,:244,230,:245,231,:246,173,:247,232,:248,116,:249,214,:250,244,:251,234,:252,168,:253,80,:254,88,:255,175,:"); + } - [Fact] - [Category("QRGenerator/AlphanumDict")] - public void validate_alphanumencdict() - { - var gen = new QRCodeGenerator(); +#if !NETFRAMEWORK // [Theory] is not supported in xunit < 2.0.0 + [Theory] + // version 1 numeric + [InlineData("1", "KWw84nkWZLMh5LqAJ/4s/4mW/08", 21)] + [InlineData("12", "+MdvzzZYQNF3d+6NuZGSmqmCmXY", 21)] + [InlineData("123", "meNWffAoC6ozzXEdDpEjixvBAME", 21)] + [InlineData("1234", "rOI2dmjilbVXsk4m2sJjAWybMto", 21)] + [InlineData("12345", "gVrbNyJNTkLCoXhLA1g1vGUlQvI", 21)] + [InlineData("123456", "TsdKS6PDgtq1b2stRT1C90DiGik", 21)] + [InlineData("1234567", "pbpVWmVQPjeRSPk/8GIlAbtPPlY", 21)] + [InlineData("12345678", "ng29QoMxhqMsygeU7t2Ic9RB2hk", 21)] + [InlineData("123456789", "Xb/EHaUUUU+22a4Hm/2Sr+O1zv0", 21)] + [InlineData("1234567890", "X0kmbmnqpAFjTuS0SQAEkphAaok", 21)] + [InlineData("1234567890123456", "afOstf4rTgaLUaHL/Vb23vzjQFM", 21)] + [InlineData("12345678901234567", "S0BOgmRblr9Bb6Lpkf62WfYIj58", 21)] + // version 2 numeric + [InlineData("123456789012345678", "qs5j+bBK3fdRgoQg1N00vUF7f0g", 25)] + [InlineData("1234567890123456789012345678901234", "mu6wUZp+uXqXGyYFduQZt38Jbu0", 25)] + // version 3 numeric + [InlineData("12345678901234567890123456789012345", "AiWiTB6xreLc514aHw4StDsomvk", 29)] + [InlineData("1234567890123456789012345678901234567890123456789012345678", "WNLD0ved5WdysFG1uqNBBV7ItKI", 29)] + // version 4 numeric + [InlineData("12345678901234567890123456789012345678901234567890123456789", "cV6Rijj6q3f/dUlDVOZD3DafrMM", 33)] + + // version 1 alphanumeric + [InlineData("A", "YUpoycThbE3FwkkHaO6GYqe9V+c", 21)] + [InlineData("AB", "UnUHZDgLdnYIy0iN31sguw2qbh8", 21)] + [InlineData("ABC", "GVB3xcSMAawwOZlq0hiF9hqVldg", 21)] + [InlineData("ABCD", "jATOwpwGVWpou3WtKiq4DX4jWkk", 21)] + [InlineData("ABCDE", "m/LrK4iP22OW9RmC2r2dnDFd4wE", 21)] + [InlineData("ABCDEF", "p8acVHkm3z751oh5yK4mBBRMUuE", 21)] + [InlineData("ABCDEFG", "md1jFcZSqDmQ2KeFTwKJVFrfZko", 21)] + [InlineData("ABCDEFGH", "XvL+fpHNqQQ2FHUCXraQw77DGns", 21)] + [InlineData("ABCDEFGHI", "k+DTXI3yht473k9lvYLMdHf0V/0", 21)] + [InlineData("ABCDEFGHIJ", "f9uful+85iSlJVJAFc5zEk04eMc", 21)] + // version 2 alphanumeric + [InlineData("ABCDEFGHIJK", "qiXut4Jz2zX8Tl9DSXxIo+bqjZY", 25)] + [InlineData("ABCDEFGHIJKL", "wSpjZmpo9CEjlxlYF18xEa6BMYM", 25)] + [InlineData("ABCDEFGHIJKLM", "utCYtAtJp+GdKS6y6A7jQuES6kA", 25)] + [InlineData("ABCDEFGHIJKLMN", "jhzNewJNcC875mlYI31BkVgx0G0", 25)] + [InlineData("ABCDEFGHIJKLMNO", "eWCSdyn3EH3uFDig1a0NUYZFlO0", 25)] + [InlineData("ABCDEFGHIJKLMNOP", "glV9FE+UQPDkplgOXFhk3Ll29pI", 25)] + [InlineData("ABCDEFGHIJKLMNOPQ", "Crlq92Pqiw8X9EIG6KFCvStNQuI", 25)] + [InlineData("ABCDEFGHIJKLMNOPQR", "lIG8/YBsD0uK+Dop6QfvD7IFdAY", 25)] + [InlineData("ABCDEFGHIJKLMNOPQRS", "rsDPSYotVP65kLAxP3fzbGqt6wc", 25)] + [InlineData("ABCDEFGHIJKLMNOPQRST", "yOb1jCKlj3wqdczHEPRdWxafvNU", 25)] + // version 3 alphanumeric + [InlineData("ABCDEFGHIJKLMNOPQRSTU", "U4YNhfjgJbprsTurHs4E7Mi2sS8", 29)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUV", "s9WCvPzYhXrwzNoVbDocQtse1w8", 29)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVW", "6vZ9rDMy1GUVEcnL2ErJFOxqWI0", 29)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWX", "C9LlmjRV+TPDkR03MlgDvo/DP+U", 29)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXY", "+EtALGm0mrDrnZVW54WdXG612P0", 29)] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ789012345", "3nFUvZ/Aa2wUdAj1zlMmSu9x4kU", 29)] + // version 4 alphanumeric + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ7890123456", "9K6EinxynS2KRum46iQsVoPgM7k", 33)] + + // version 1 binary + [InlineData("a", "zd6kApf0BQSE5W8fhCijkz6wzKA", 21)] + [InlineData("ab", "mXAUC/dqcqqj6SzC+Us6NiYzzCM", 21)] + [InlineData("abc", "6Y3HGyOFxhZUYINks/hzE2DjulM", 21)] + [InlineData("abcd", "ssvlMmEub85t1d0R/aZG+Qgpa+0", 21)] + [InlineData("abcde", "At93DjDyAtIkTCpOwD3p/lSqFz4", 21)] + [InlineData("abcdef", "Q8BU1lCJJA/UesKGvszQTptskSk", 21)] + [InlineData("abcdefg", "2AceZvcjIpEBCh5FIc1esXsaEY4", 21)] + // version 2 binary + [InlineData("abcdefgh", "kl8bx15B4ApauURa0nlD71NPk+I", 25)] + [InlineData("abcdefghi", "v9rI0/2a8nYxM9MerzxSCwT6EKs", 25)] + [InlineData("abcdefghij", "yE59+LfDLQCt2VXCuhnz9aFIoMk", 25)] + [InlineData("abcdefghijk", "nVne+lyjPV5XDMKqa0+oNfxZTgI", 25)] + [InlineData("abcdefghijkl", "QUeDHmjQDyHvbe5r8tViXxBcHv0", 25)] + [InlineData("abcdefghijklm", "WtN1tTti8hV4vvH5vX6obIPdjpM", 25)] + [InlineData("abcdefghijklmn", "AT5SPNUPL3wG0r4XXPBzSAK2sIE", 25)] + // version 3 binary + [InlineData("abcdefghijklmno", "N04AFOJlXQRjeXijWoy4rsBNZGg", 29)] + [InlineData("abcdefghijklmnop", "a4tgwBApGX0+P4yiwR/wUtLAxQA", 29)] + [InlineData("abcdefghijklmnopq", "MFLd+exCRrUZkqfw5UTqY2QZ1n0", 29)] + [InlineData("abcdefghijklmnopqr", "aSYOJXfFAjxrtBWnBQqHWrC8Zv0", 29)] + [InlineData("abcdefghijklmnopqrs", "K9Uic6+NO2rPy/Hfo4fEhXkUw2Q", 29)] + [InlineData("abcdefghijklmnopqrst", "eKVJvIH8J1waEb3UHRdXYAWLezc", 29)] + [InlineData("abcdefghijklmnopqrstu", "ylmFLWV1grM2MoTFpdngo05fdyI", 29)] + [InlineData("abcdefghijklmnopqrstuv", "Z0IETxnf8x+pTU2nuj1hxg2G/pQ", 29)] + [InlineData("abcdefghijklmnopqrstuvw", "oHzGRWtkI+a30AF5JILT6HON7Zc", 29)] + [InlineData("abcdefghijklmnopqrstuvwx", "NRIoT6rGd3HWrBq4JhBWvbwYp9g", 29)] + // version 4 binary + [InlineData("abcdefghijklmnopqrstuvwxy", "RMSyMOBpdBYphJPkXR/xA/ekPoo", 33)] + [InlineData("abcdefghijklmnopqrstuvwxyz", "BAMiY251UapecfI+v2C3cX2EBH4", 33)] + // version 5 binary + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghi", "yV9Cd3xiW2HRzSIMq3eLTIrdqVQ", 37)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghij", "mV/R+gMAwN+lO8ByXhU5IyZp39Y", 37)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijk", "sIb5hBRamy+MIgaFakCCGnDM9yU", 37)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijkl", "2/PZLsxe4c/R/tStrn9pcB8EUOQ", 37)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklm", "KFReyVpr4rq5c+ELZBt/ZuhQkYM", 37)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmn", "IwlWmCnXp0FSr+WUp/igMuQKHQo", 37)] + // version 7 binary + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg", "bisFBjhANRxoF9JDCBSODvsSKqk", 45)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefgh", "MwRnhkqr5CM17xtcQycytd+d+Fs", 45)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi", "PFlhVI0La4/qOweduCP2WfedoCQ", 45)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij", "ZwMdK51id9A99IxefE01o5ZtkN4", 45)] + [InlineData("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", "HM6MMwWDmJ0PTLLBWzIo7Q0YvmA", 45)] + public void can_encode_various_strings_ecc_h(string input, string expectedHash, int expectedSize) + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode(input, ECCLevel.H); + (qrData.ModuleMatrix.Count - 8).ShouldBe(expectedSize); // exclude padding + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + var hash = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(result)); + var hashString = Convert.ToBase64String(hash); + hashString.TrimEnd('=').ShouldBe(expectedHash); + } - var checkString = string.Empty; - var gField = gen.GetType().GetField("alphanumEncDict", BindingFlags.NonPublic | BindingFlags.Static); - foreach (var listitem in (Dictionary)gField.GetValue(gen)) - { - checkString += $"{listitem.Key},{listitem.Value}:"; - } - checkString.ShouldBe("0,0:1,1:2,2:3,3:4,4:5,5:6,6:7,7:8,8:9,9:A,10:B,11:C,12:D,13:E,14:F,15:G,16:H,17:I,18:J,19:K,20:L,21:M,22:N,23:O,24:P,25:Q,26:R,27:S,28:T,29:U,30:V,31:W,32:X,33:Y,34:Z,35: ,36:$,37:%,38:*,39:+,40:-,41:.,42:/,43::,44:"); - } + [Theory] + // Version 1 + [InlineData(17, ECCLevel.L, "iOaoY7YsHAYGNRn+Tpnt74IQoVw=", 21)] + [InlineData(14, ECCLevel.M, "JV2XYoq8nt/lWipVkwvvSbNvFVQ=", 21)] + [InlineData(11, ECCLevel.Q, "44vd54SCPFEevWN9PKC5swEpVmU=", 21)] + [InlineData(7, ECCLevel.H, "FvR2FAU+4sltHMS969/Y1FAHZRA=", 21)] + // Version 2 + [InlineData(32, ECCLevel.L, "vM4eIrKbner3NxjRznd6kZLbyck=", 25)] + [InlineData(26, ECCLevel.M, "mesaTID5N92ar2fyElorp7zcSVg=", 25)] + [InlineData(20, ECCLevel.Q, "mg1Z+VPVuxMoGwvgRzJrW4NHehA=", 25)] + [InlineData(14, ECCLevel.H, "6T0I6Z9AmN9yNIvan82NQqAMATc=", 25)] + // Version 3 + [InlineData(53, ECCLevel.L, "O8Wkal/iDmCnENBubqR0HXOo/RY=", 29)] + [InlineData(42, ECCLevel.M, "NCjzwIm3l5urwU4EcFGK5DD1y9U=", 29)] + [InlineData(32, ECCLevel.Q, "kR+4FNybKyAiGDLPDnzIslvjypQ=", 29)] + [InlineData(24, ECCLevel.H, "ZamyoGRJG7mGMY6nzz8r3+q0z18=", 29)] + // Version 4 + [InlineData(78, ECCLevel.L, "P3Dx7gDjD2L94wPyL23AO/z5+Yk=", 33)] + [InlineData(62, ECCLevel.M, "wDUcQmVTCcTx6sStDlPG4Wsn5FU=", 33)] + [InlineData(46, ECCLevel.Q, "3mYP2cHqQysy93UC4NGUnNwfj10=", 33)] + [InlineData(34, ECCLevel.H, "5ovh+NFiGh6soAuNNTWqxenM8cw=", 33)] + // Version 5 + [InlineData(106, ECCLevel.L, "0vZswNhwdcCcj2GpwucfkcnlG/M=", 37)] + [InlineData(84, ECCLevel.M, "AWlV4NsbRtkmH2/vfBxTahIiG7U=", 37)] + [InlineData(60, ECCLevel.Q, "8w35kxFqvcMajba9IvRhjbOn0Js=", 37)] + [InlineData(44, ECCLevel.H, "+d0gLH3v9FG+w/hhv+zDm2Y3IVw=", 37)] + // Version 6 + [InlineData(134, ECCLevel.L, "CNyyNDIylrMi97DwuNh6JAgHlw8=", 41)] + [InlineData(106, ECCLevel.M, "z4LUkv75O26FLaVo823TMLv9Owg=", 41)] + [InlineData(74, ECCLevel.Q, "NgCKIxbeuSt24C9M067nDGopKgU=", 41)] + [InlineData(58, ECCLevel.H, "Bzp923oooHYWoQfETFENmb5wup0=", 41)] + // Version 7 + [InlineData(154, ECCLevel.L, "ftMeEbWj6D0lyOBVsAnTCq0UV0s=", 45)] + [InlineData(122, ECCLevel.M, "zif9uHXnPgo+OeIN95xU3iqcexk=", 45)] + [InlineData(86, ECCLevel.Q, "wApf2GzMYIQlYw4ws3k6Wi1DqMU=", 45)] + [InlineData(64, ECCLevel.H, "i8llCv2L4dwlW5E8+mswsAa+Zo4=", 45)] + // Version 8 + [InlineData(192, ECCLevel.L, "DSph/W1Nq2VAKFxgRq0VeqTP54g=", 49)] + [InlineData(152, ECCLevel.M, "j06phyT/k6pXqf935BuaMxUjckk=", 49)] + [InlineData(108, ECCLevel.Q, "RufVav4xUUuL/K5ELnH3/qUrEf8=", 49)] + [InlineData(84, ECCLevel.H, "oG538pE6ac81I2of3LzIHQ6+Dxg=", 49)] + // Version 9 + [InlineData(230, ECCLevel.L, "LBs30yL9Rec1qFdPwKz4nBrDraY=", 53)] + [InlineData(180, ECCLevel.M, "c0DN8hFoX6SEkVKr/yVA79SZE4g=", 53)] + [InlineData(130, ECCLevel.Q, "XdZ3zNyz14Sq0fv9KjonZK7ok04=", 53)] + [InlineData(98, ECCLevel.H, "9NE1egSXCdGY8AiY4LhHM6sO/jA=", 53)] + // Version 10 + [InlineData(271, ECCLevel.L, "gyox9Nk2DCPzVeL2E1V/P5XsuNY=", 57)] + [InlineData(213, ECCLevel.M, "iqS7CNYuwZpw47/SnM8JAcWkhCE=", 57)] + [InlineData(151, ECCLevel.Q, "vVdh2R+yWmSeDc7iCKonTTcs4ok=", 57)] + [InlineData(119, ECCLevel.H, "n79CbR/JZZC30sDIDjdFAgurzR4=", 57)] + // Version 11 + [InlineData(321, ECCLevel.L, "R77LLVhO+/YE8WKmn3CV9f/I9ZY=", 61)] + [InlineData(251, ECCLevel.M, "l/IFWD6Pkm1TZHFc4ZuFLWDrfdc=", 61)] + [InlineData(177, ECCLevel.Q, "SxVElF8qBWe0oXXGn57CoI6iglo=", 61)] + [InlineData(137, ECCLevel.H, "GrHJ2EiDMJ/cXpjcITvypJZZGrY=", 61)] + // Version 12 + [InlineData(367, ECCLevel.L, "rCv4hIrv0obcHALDSvzN/5zwCfg=", 65)] + [InlineData(287, ECCLevel.M, "mBC3lYhpNuCa2TbD/h+F6gFH8f4=", 65)] + [InlineData(203, ECCLevel.Q, "2Gpr+HihG8dDshcf96n2lNopsiM=", 65)] + [InlineData(155, ECCLevel.H, "eMjatzihLLJH1KZ56GAmaXyf/os=", 65)] + // Version 13 + [InlineData(425, ECCLevel.L, "mmPOWpjyRZWVC+JRJFDpufEqbUk=", 69)] + [InlineData(331, ECCLevel.M, "nujBdyCZyO4HoHL4uLYIucd/MA4=", 69)] + [InlineData(241, ECCLevel.Q, "IT+VAECAcwuZqJdQft5fWo/UTMs=", 69)] + [InlineData(177, ECCLevel.H, "7JczSXSWYg5XXPhdqLx4Lb411lU=", 69)] + // Version 14 + [InlineData(458, ECCLevel.L, "a/hMcMmEajVBC3kj8ILzRdGR4t0=", 73)] + [InlineData(362, ECCLevel.M, "M7D1Cm0FeVbNeiZd+yUPp/8lDfU=", 73)] + [InlineData(258, ECCLevel.Q, "KAnpA9g4esUdsXHLBpIVvbJ/Dsw=", 73)] + [InlineData(194, ECCLevel.H, "gs48PFtIXdBTsNB5CIDK4IopcMU=", 73)] + // Version 15 + [InlineData(520, ECCLevel.L, "L1/Q9lMcmGeNwY4RbBTfFKk2CAQ=", 77)] + [InlineData(412, ECCLevel.M, "7Z4o8qbi+HXAh5wSBlg/KO8VWl8=", 77)] + [InlineData(292, ECCLevel.Q, "v52Vt1lJpaEOWkfIsmRMeF2VkZ4=", 77)] + [InlineData(220, ECCLevel.H, "0c8GsO3CIWhcJYcQDE92+l+w7rQ=", 77)] + // Version 16 + [InlineData(586, ECCLevel.L, "M66FY6iMOawr2JoEInl0KBKQ1nI=", 81)] + [InlineData(450, ECCLevel.M, "5u8qfYfrBGxzyesU/xepVqwaWWw=", 81)] + [InlineData(322, ECCLevel.Q, "QnNtWtlQDmt6cu535YqceOZAyVY=", 81)] + [InlineData(250, ECCLevel.H, "w2O4eKcEL43ibEH/dDzbNqGDFaM=", 81)] + // Version 17 + [InlineData(644, ECCLevel.L, "7uG763m0mGPJdY9nwquzdiR4Yu8=", 85)] + [InlineData(504, ECCLevel.M, "FJuxPTwkgTFHIiFOKfdMMjins2Y=", 85)] + [InlineData(364, ECCLevel.Q, "JPuf2oD8xEeJSY/bhIO7VCbDxfI=", 85)] + [InlineData(280, ECCLevel.H, "iepqbSMD9KO0jdaBHdDD3CN/ELA=", 85)] + // Version 18 + [InlineData(718, ECCLevel.L, "tg9fRcelrfpz1muMC3bp9Rd+d+Q=", 89)] + [InlineData(560, ECCLevel.M, "ZiJ3ALPxKefddqcbFsaLVtaqu4M=", 89)] + [InlineData(394, ECCLevel.Q, "TetEWsqYm2DnePzsBN2n2TZI1qw=", 89)] + [InlineData(310, ECCLevel.H, "g3SqiNtegQKKWz0fphMJNbMnauI=", 89)] + // Version 19 + [InlineData(792, ECCLevel.L, "G3wlYoJhuxgOMAhwlBSlenIPzQE=", 93)] + [InlineData(624, ECCLevel.M, "zlQfupN9mxSqabm5IH0Au5UltHA=", 93)] + [InlineData(442, ECCLevel.Q, "ZTqF6EL1yCtaRxB2/fwuOUQVyDo=", 93)] + [InlineData(338, ECCLevel.H, "R2zVkSuv/xkSn5tz4RW/8z1Tu+U=", 93)] + // Version 20 + [InlineData(858, ECCLevel.L, "picn/dsww2hy+2gWQCFJfWRryvM=", 97)] + [InlineData(666, ECCLevel.M, "65jjl86TbBbuzJk6n42DYMGWVtI=", 97)] + [InlineData(482, ECCLevel.Q, "78k1kZhh228wPC1HNLuHR2E2rXI=", 97)] + [InlineData(382, ECCLevel.H, "cjCcbhd7GuNpePeTVeEXS11ZrXk=", 97)] + // Version 21 + [InlineData(929, ECCLevel.L, "88t3Y6RQA+g+6r8mp9RUuMkZGP0=", 101)] + [InlineData(711, ECCLevel.M, "Xlq1Xfk881mzrCO+Iu8kK8brGBg=", 101)] + [InlineData(509, ECCLevel.Q, "KkN2utAu40CZuKCUN0jLWJ+Vd6o=", 101)] + [InlineData(403, ECCLevel.H, "L44GKy1dAMakEhA4UO3rZscrjys=", 101)] + // Version 22 + [InlineData(1003, ECCLevel.L, "QN5/o8D8VuuH8hDP+DIItgKwPb0=", 105)] + [InlineData(779, ECCLevel.M, "K6ss7doERSJbYd2EAWWgB8q/tjQ=", 105)] + [InlineData(565, ECCLevel.Q, "uO4I8RZ1QBgOdlV36LGFCHeNOzk=", 105)] + [InlineData(439, ECCLevel.H, "wdAxJat/xdz5D/A53Twe7LijnHg=", 105)] + // Version 23 + [InlineData(1091, ECCLevel.L, "ZIkAmmyIUotcDSfA4APqqBrb1WY=", 109)] + [InlineData(857, ECCLevel.M, "7h/MVR1ognfK4SVDRmfgyy7UJVA=", 109)] + [InlineData(611, ECCLevel.Q, "NYW8AFfV48ojWSAiZjnMYR98o78=", 109)] + [InlineData(461, ECCLevel.H, "7rQgH3LE/YyOjpqH3XficevFSaU=", 109)] + // Version 24 + [InlineData(1171, ECCLevel.L, "0pNvS9HfwsAOLG6/U1PXKMyoc6I=", 113)] + [InlineData(911, ECCLevel.M, "555aDmkhsRGE2li1z81j6mVMh7s=", 113)] + [InlineData(661, ECCLevel.Q, "UJQmA6QoWlN3r8BEF+zIFvWsoJ4=", 113)] + [InlineData(511, ECCLevel.H, "emTvRmvoSWFRHLljWXOzvUjpLX0=", 113)] + // Version 25 + [InlineData(1273, ECCLevel.L, "vf+HWAPl5vJXIKHrZCPHjAInuo4=", 117)] + [InlineData(997, ECCLevel.M, "v+H4HXOL3tO5/QIsK1IYPGu+zA0=", 117)] + [InlineData(715, ECCLevel.Q, "rc+LXSs8ILA84TabFJ5b45gX0n8=", 117)] + [InlineData(535, ECCLevel.H, "ZidXJ+kT23SwS9+xbLZ755ATt4g=", 117)] + // Version 26 + [InlineData(1367, ECCLevel.L, "QPNW+iYKQdJClPaZsuTUju6dsQE=", 121)] + [InlineData(1059, ECCLevel.M, "NLTDTTGBmvzR6TOUSxTF/EY4oLI=", 121)] + [InlineData(751, ECCLevel.Q, "MoOh9EA/7kESiuzy6YHJWcjujMM=", 121)] + [InlineData(593, ECCLevel.H, "16reau4y5ukinTo0YfM1ToP+/nE=", 121)] + // Version 27 + [InlineData(1465, ECCLevel.L, "NeSmv01ZOFMeRCxIu6AshMdonOM=", 125)] + [InlineData(1125, ECCLevel.M, "4xKzxZ7KvzpFAlFmEXYXDHuKh2A=", 125)] + [InlineData(805, ECCLevel.Q, "LWbwDKIZKwqnfJQncZM+SXJ2qmg=", 125)] + [InlineData(625, ECCLevel.H, "ks+3sSJU4VFavu6ILO7zFVQZVqc=", 125)] + // Version 28 + [InlineData(1528, ECCLevel.L, "h64k62PMqcq+hpKSUEFbfNwOlxY=", 129)] + [InlineData(1190, ECCLevel.M, "H8mNI2+l1xp6LAJKc54t6BcTgXE=", 129)] + [InlineData(868, ECCLevel.Q, "s+IrQoE960X/7R6mFQyw6w3d0C4=", 129)] + [InlineData(658, ECCLevel.H, "ugGZT7++E0kKqEi1lOwpgGIuilU=", 129)] + // Version 29 + [InlineData(1628, ECCLevel.L, "ZJ2nILLA7z8tcMK/NUayp0gd+Ws=", 133)] + [InlineData(1264, ECCLevel.M, "oVnYNf73ATNLaLNV+Kcsk/kkJ5g=", 133)] + [InlineData(908, ECCLevel.Q, "G/4ZQB/lgaaP8cfzy6tyQkA4LCk=", 133)] + [InlineData(698, ECCLevel.H, "snR+O40DDNFn6tGio9X3Qx7Yj6I=", 133)] + // Version 30 + [InlineData(1732, ECCLevel.L, "QnOSXQy6rgnX3mOEFilBMYagxd4=", 137)] + [InlineData(1370, ECCLevel.M, "JV+jGwxg94rTaQB1pYEbyznyBg4=", 137)] + [InlineData(982, ECCLevel.Q, "nI2Xd3XP9ozg+YUns5VN1JvyLqg=", 137)] + [InlineData(742, ECCLevel.H, "YZKS3IH1kVNvndDJHxQGdRk6WNY=", 137)] + // Version 31 + [InlineData(1840, ECCLevel.L, "jjvNCmxgZzQnl0gtfx8AXGDzr64=", 141)] + [InlineData(1452, ECCLevel.M, "8P5rLwbI3czsK65jSnWYHK540ps=", 141)] + [InlineData(1030, ECCLevel.Q, "Z/+XPRRYe4AL90fdeD2pd9BtEJo=", 141)] + [InlineData(790, ECCLevel.H, "cauLAUrqzrUlDce6QvLc0QDVb8o=", 141)] + // Version 32 + [InlineData(1952, ECCLevel.L, "heo6yDq1jE3lrJBGQRWFq6Yxw4Y=", 145)] + [InlineData(1538, ECCLevel.M, "Owl+uFMlPWFUDa14YKPHl6Iz2rw=", 145)] + [InlineData(1112, ECCLevel.Q, "iH25k4mUj1LZFfH9RlRU7w1mvgU=", 145)] + [InlineData(842, ECCLevel.H, "u6fkuEiZbHyROePmzV22/fi4BUQ=", 145)] + // Version 33 + [InlineData(2068, ECCLevel.L, "d9ATU4zloW1TWbDyFUQRnstXX80=", 149)] + [InlineData(1628, ECCLevel.M, "jJxIdcbC4JK6ilHF3tCLUN2z12Q=", 149)] + [InlineData(1168, ECCLevel.Q, "ODr9TeLZGwhJOkKiFTNfzxGeD5E=", 149)] + [InlineData(898, ECCLevel.H, "FxTOLRMB5YQw1y4Z2mxTOv+5p3g=", 149)] + // Version 34 + [InlineData(2188, ECCLevel.L, "hOS3RBFGQAIBqVxI1dJD3DMbPiM=", 153)] + [InlineData(1722, ECCLevel.M, "6ljLKSekU2f3IZSVYt2SmkCwxcQ=", 153)] + [InlineData(1228, ECCLevel.Q, "CMPPw3e2cUtxuPp6vyW4FLgltdg=", 153)] + [InlineData(958, ECCLevel.H, "ZHTja2LTLTgrd9Ha0awn5HiB9Pk=", 153)] + // Version 35 + [InlineData(2303, ECCLevel.L, "pmSX3DjwSMpia+KAM+MYi+jAyN4=", 157)] + [InlineData(1809, ECCLevel.M, "Uk6fudf3ij96QeYbDKMqyuC7ccY=", 157)] + [InlineData(1283, ECCLevel.Q, "Sss6YeZZjl/eA019B0vHOMAivG0=", 157)] + [InlineData(983, ECCLevel.H, "NXJCn5THFStoGeJdQ3gqVfpSQic=", 157)] + // Version 36 + [InlineData(2431, ECCLevel.L, "3aSKsgEZlsJ6PisZN9f55NPecHg=", 161)] + [InlineData(1911, ECCLevel.M, "OmKYn++0akH80oMbs/CAUCoHFsY=", 161)] + [InlineData(1351, ECCLevel.Q, "WWyI18sPU1e4z7/D6/tND0oJhps=", 161)] + [InlineData(1051, ECCLevel.H, "m38wtnevvqtYxDfS4dM7xDMaI/M=", 161)] + // Version 37 + [InlineData(2563, ECCLevel.L, "YV6EWRw9/HGfVO0COUoQ94uGRRg=", 165)] + [InlineData(1989, ECCLevel.M, "/KjlKPlECYqj/pX/Dz3wl9lLKsE=", 165)] + [InlineData(1423, ECCLevel.Q, "mI+9aaDdXQHxir65csXwE487Zvg=", 165)] + [InlineData(1093, ECCLevel.H, "f+oGwtL745K4S3x25qNBY8XCGyA=", 165)] + // Version 38 + [InlineData(2699, ECCLevel.L, "jFAE8Cw77UN/uf8rndzymM7Idwc=", 169)] + [InlineData(2099, ECCLevel.M, "roNsVHUcMngjE4/GyJop++9eeRs=", 169)] + [InlineData(1499, ECCLevel.Q, "G2/TnwE1HSnoTytk0mr4552oIos=", 169)] + [InlineData(1139, ECCLevel.H, "fou4BP/tpD9oxj7KeXyzZ6Fo+xc=", 169)] + // Version 39 + [InlineData(2809, ECCLevel.L, "IZ2L6FrpTQdjqls020r8YEWiFU8=", 173)] + [InlineData(2213, ECCLevel.M, "iybO3rmtGmgVRcOYkDnfrH7Scpo=", 173)] + [InlineData(1579, ECCLevel.Q, "O8jQuoPVybJuJy9ULv/xfJKA29Y=", 173)] + [InlineData(1219, ECCLevel.H, "FD2FN7bSh8ispau30YQRyNN5LL0=", 173)] + // Version 40 + [InlineData(2953, ECCLevel.L, "a+80y4pPAhCywuraFrnMSTFRRmo=", 177)] + [InlineData(2331, ECCLevel.M, "jLKuYZ7beIij+5j9Ko6GRxZVzaA=", 177)] + [InlineData(1663, ECCLevel.Q, "G1vhiI8anCkTOgeQPQAVH3xcSk8=", 177)] + [InlineData(1273, ECCLevel.H, "A0HAgMWn4TvnFfSnnEhQ0cVXcNU=", 177)] + public void can_encode_various_strings_various_ecc(int inputChars, ECCLevel eccLevel, string expectedHash, int expectedSize) + { + var input = new string('a', inputChars); + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode(input, eccLevel); + (qrData.ModuleMatrix.Count - 8).ShouldBe(expectedSize); // exclude padding + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + var hash = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(result)); + var hashString = Convert.ToBase64String(hash); + hashString.ShouldBe(expectedHash); + } +#endif - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_recognize_enconding_numeric() - { - var gen = new QRCodeGenerator(); - MethodInfo method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method.Invoke(gen, new object[] { "0123456789", false }); + [Fact] + [Category("QRGenerator/AlphanumDict")] + public void validate_alphanumencdict() + { + var gen = new QRCodeGenerator(); - result.ShouldBe(1); + var checkString = string.Empty; + var gField = gen.GetType().GetField("_alphanumEncDict", BindingFlags.NonPublic | BindingFlags.Static); + foreach (var listitem in (Dictionary)gField.GetValue(gen)) + { + checkString += $"{listitem.Key},{listitem.Value}:"; } + checkString.ShouldBe("0,0:1,1:2,2:3,3:4,4:5,5:6,6:7,7:8,8:9,9:A,10:B,11:C,12:D,13:E,14:F,15:G,16:H,17:I,18:J,19:K,20:L,21:M,22:N,23:O,24:P,25:Q,26:R,27:S,28:T,29:U,30:V,31:W,32:X,33:Y,34:Z,35: ,36:$,37:%,38:*,39:+,40:-,41:.,42:/,43::,44:"); + } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_recognize_enconding_numeric() + { + var gen = new QRCodeGenerator(); + var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); + var result = (int)method.Invoke(gen, new object[] { "0123456789", false }); - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_recognize_enconding_alphanumeric() - { - var gen = new QRCodeGenerator(); - MethodInfo method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method.Invoke(gen, new object[] { "0123456789ABC", false }); + result.ShouldBe(1); + } - result.ShouldBe(2); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_recognize_enconding_alphanumeric() + { + var gen = new QRCodeGenerator(); + var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); + var result = (int)method.Invoke(gen, new object[] { "0123456789ABC", false }); - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_recognize_enconding_forced_bytemode() - { - var gen = new QRCodeGenerator(); - MethodInfo method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method.Invoke(gen, new object[] { "0123456789", true }); + result.ShouldBe(2); + } - result.ShouldBe(4); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_recognize_enconding_forced_bytemode() + { + var gen = new QRCodeGenerator(); + var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); + var result = (int)method.Invoke(gen, new object[] { "0123456789", true }); - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_recognize_enconding_byte() - { - var gen = new QRCodeGenerator(); - MethodInfo method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method.Invoke(gen, new object[] { "0123456789äöüß", false }); + result.ShouldBe(4); + } - result.ShouldBe(4); - } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_numeric() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("123", ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000001110110000001010101100000000000110111100001101110000000000101111010011000001111000000000011101111100010011010000000000000000111110010101100000000111111100010111110001000000001000001000011101110010000000010111010101110110110100000000101110101011100011100000000001011101001100010001110000000010000010101001101010100000000111111101101000001110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_recognize_enconding_byte() + { + var gen = new QRCodeGenerator(); + var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); + var result = (int)method.Invoke(gen, new object[] { "0123456789äöüß", false }); - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_numeric_2() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("1234567", ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000000100000000101010111100000000010110110100001101000000000000101110001101000001111000000001110111111000010010010000000000000000100110010011100000000111111100100111111101000000001000001000111101110110000000010111010110110110110100000000101110101101100011100000000001011101000100010011110000000010000010100001101010100000000111111101111000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + result.ShouldBe(4); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_numeric_3() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("12345678901", ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111010111011111110000000010000010001100100000100000000101110101101001011101000000001011101011001010111010000000010111010100100101110100000000100000100111101000001000000001111111010101011111110000000000000000000110000000000000000111100101111110011101000000001110010101011110011110000000010011010000100000010000000000010010010111001110001000000000101101011001001000100000000000000000111100100100100000000111111100111100101101000000001000001001100001101010000000010111010001011111000100000000101110101011001011010000000001011101011001011011000000000010000010111001001101100000000111111101000010010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_numeric() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("123", ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000001110110000001010101100000000000110111100001101110000000000101111010011000001111000000000011101111100010011010000000000000000111110010101100000000111111100010111110001000000001000001000011101110010000000010111010101110110110100000000101110101011100011100000000001011101001100010001110000000010000010101001101010100000000111111101101000001110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_alphanumeric() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("123ABC", ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111010111011111110000000010000010001100100000100000000101110101101001011101000000001011101011001010111010000000010111010100100101110100000000100000100111101000001000000001111111010101011111110000000000000000000110000000000000000111100101111110011101000000000111100010011110001110000000000100010100100000001000000000011110011111001110011000000001111101110101001000000000000000000000111100100100100000000111111100001100100110000000001000001000100001111110000000010111010010011111010100000000101110101111001011110000000001011101010101011000000000000010000010111001000010000000000111111101010010010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_numeric_2() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("1234567", ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000000100000000101010111100000000010110110100001101000000000000101110001101000001111000000001110111111000010010010000000000000000100110010011100000000111111100100111111101000000001000001000111101110110000000010111010110110110110100000000101110101101100011100000000001011101000100010011110000000010000010100001101010100000000111111101111000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_byte_long() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("https://github.com/codebude/QRCoder/blob/f89aa90081f369983a9ba114e49cc6ebf0b2a7b1/QRCoder/Framework4.0Methods/Stream4Methods.cs", ECCLevel.H); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110111011110110000101001110011101111011110001011011111110000000010000010111110111110001010010001100010100000011000011010000010000000010111010110010100110110000100110001010110100101010111010111010000000010111010000110010111011000101111110011010101000100101010111010000000010111010011010111000011000101111100001101111110001110010111010000000010000010110101101010101100011000100100100001011101100010000010000000011111110101010101010101010101010101010101010101010101011111110000000000000000111011011010010111001000100010111011010101000000000000000000000111010111110001111111100111111110110000111011110011111001110000000011111101111010101011111100011110101010001011000111000110100000000000000101111000110111101010010011011000010000110010110101000000110000000000000001111100010111100110100010100011010111100000001000100010000000000101010011011100110011111101000111110001100011111011011011110000000010010001110011110000101111001100100011001111110010010011010100000000001010011010001011011011010111011001110110000001001100101011110000000010110000111100111100010110011101110000001101111001000010110010000000010101010100011010111011110111010010100010100000110111111011010000000000110101111110101110100001101100010101110110010111000011010000000000001011111111001010110101000111001001011011000101011111101010110000000000010000000110100110010011100110100000100110111101001011110000000000010101110011111100011001000110101101111001011000011001001001100000000000100000101111001101001100001000010110010111000001000010010100000000010000011000010110111001110111011010010101000111011110100111110000000001100000100110001100100010101111011011100010110101000010000010000000000001111101011110111110100010100101010101111000010000000011110000000011010100010000111011000110000110001011000111110011010011100100000000010010011011110011110011001101110011001110001111110000000011010000000011101000110001100001110010010001011000000010101101110010110100000000010111111101100100010001000111111100111011101000010101111101000000000011001000110101111011001011001000101101011111100001101000100000000000010001010100000111101011101111010101010000000011110011010110110000000010111000100001011000010010011000100000100110101000011000100100000000000001111101111110100000010101111111100001001001110001111111100000000010111001011011001100001101010001000100111111100101011011000000000000000000011111101001100110000110100010001111001111101100111010110000000010101101101010110010011111101010000100100010000110001011010000000000010100011111001110010000010000111111110110001110111111011111010000000000001000000111110111001100111011011010100110100111011110010000000000000110111001011011100010111100000000010010011111010100110111110000000001110000110101101001100001001000111010011100110001111001100110000000001100011100110101100100101001010001100110101000111111101111000000000011101100100001000100011101010100111110101110010101001001010100000000011111110000111010111100001011011101000110100100101000101001010000000010111101100010010110000011101011011101101101101100001011000110000000010101111001111111101011010100000001000100101001010110110101000000000000011101100001001011001110010110111100101110010100000011001100000000010001111010011100000100100110111011011010111011010111110001110000000010000001111101010111111110001110110001011111101110000100000110000000000100010110000000101000111001011000011100011001011111011111110000000010110101101100011101011100001111101011111111000110010000011000000000000111111100010001000110100000001101111110101011011110011010110000000011101000111010011011110100001010110011111101010001001000000010000000011110011110100001110111110011111101110100001001111011111101110000000000000000100110000001011111101000100101001101100110001000110100000000011111110011101111000110110011010110001110011111101111010111110000000010000010010101100101110011001000111101111010111001011000100110000000010111010100001010110001101111111101101000111010111111111101000000000010111010101001111101101011110000100011100011110011011111101010000000010111010100101010001000010100001001101000100001110100010001010000000010000010001010111111111001100110111100101011111000010110000010000000011111110001011000110010001101010000101110110000110101000011110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_numeric_3() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("12345678901", ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111010111011111110000000010000010001100100000100000000101110101101001011101000000001011101011001010111010000000010111010100100101110100000000100000100111101000001000000001111111010101011111110000000000000000000110000000000000000111100101111110011101000000001110010101011110011110000000010011010000100000010000000000010010010111001110001000000000101101011001001000100000000000000000111100100100100000000111111100111100101101000000001000001001100001101010000000010111010001011111000100000000101110101011001011010000000001011101011001011011000000000010000010111001001101100000000111111101000010010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_byte() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("äöü", ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111001011011111110000000010000010011100100000100000000101110101101101011101000000001011101001010010111010000000010111010001010101110100000000100000100000101000001000000001111111010101011111110000000000000000110110000000000000000111011111111011000100000000001001110001100010000010000000010011110001010001001000000000110011010000001000110000000001110001111001010110110000000000000000111101010011100000000111111101111011100110000000001000001010011101110010000000010111010110101110010100000000101110100110001000110000000001011101011001000100010000000010000010100000100011000000000111111101110101010111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_alphanumeric() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("123ABC", ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111010111011111110000000010000010001100100000100000000101110101101001011101000000001011101011001010111010000000010111010100100101110100000000100000100111101000001000000001111111010101011111110000000000000000000110000000000000000111100101111110011101000000000111100010011110001110000000000100010100100000001000000000011110011111001110011000000001111101110101001000000000000000000000111100100100100000000111111100001100100110000000001000001000100001111110000000010111010010011111010100000000101110101111001011110000000001011101010101011000000000000010000010111001000010000000000111111101010010010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_utf8() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("https://en.wikipedia.org/wiki/🍕", ECCLevel.L, true, false, QRCodeGenerator.EciMode.Utf8); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110101011010011101111111000000001000001001111100001110100000100000000101110101110000011000010111010000000010111010111010111100101011101000000001011101010011010111010101110100000000100000100011010001110010000010000000011111110101010101010101111111000000000000000000101000011100000000000000000111100101011110101011100111010000000001011000101011111010011101010000000001010011101111101001111011101000000000111011011110000010001100000100000000000000010011010101100000000000000000001100110101011011111001101110000000000000011100001010101010110101000000000000111001011100110111111110011000000001110101011001011001000100011000000000000101010100001010111111000000000000010111010101001111100000001110000000000010110100010111111100100010100000000011101111010011101111111101010000000000000000110000001000100010010000000001111111001100011001010101101000000000100000100111111111011000111000000000010111010010100011010111110111000000001011101010110100011100101011000000000101110101100101111100101111010000000010000010111011001111000001101000000001111111011110000100000110101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_byte_long() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("https://github.com/codebude/QRCoder/blob/f89aa90081f369983a9ba114e49cc6ebf0b2a7b1/QRCoder/Framework4.0Methods/Stream4Methods.cs", ECCLevel.H); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110111011110110000101001110011101111011110001011011111110000000010000010111110111110001010010001100010100000011000011010000010000000010111010110010100110110000100110001010110100101010111010111010000000010111010000110010111011000101111110011010101000100101010111010000000010111010011010111000011000101111100001101111110001110010111010000000010000010110101101010101100011000100100100001011101100010000010000000011111110101010101010101010101010101010101010101010101011111110000000000000000111011011010010111001000100010111011010101000000000000000000000111010111110001111111100111111110110000111011110011111001110000000011111101111010101011111100011110101010001011000111000110100000000000000101111000110111101010010011011000010000110010110101000000110000000000000001111100010111100110100010100011010111100000001000100010000000000101010011011100110011111101000111110001100011111011011011110000000010010001110011110000101111001100100011001111110010010011010100000000001010011010001011011011010111011001110110000001001100101011110000000010110000111100111100010110011101110000001101111001000010110010000000010101010100011010111011110111010010100010100000110111111011010000000000110101111110101110100001101100010101110110010111000011010000000000001011111111001010110101000111001001011011000101011111101010110000000000010000000110100110010011100110100000100110111101001011110000000000010101110011111100011001000110101101111001011000011001001001100000000000100000101111001101001100001000010110010111000001000010010100000000010000011000010110111001110111011010010101000111011110100111110000000001100000100110001100100010101111011011100010110101000010000010000000000001111101011110111110100010100101010101111000010000000011110000000011010100010000111011000110000110001011000111110011010011100100000000010010011011110011110011001101110011001110001111110000000011010000000011101000110001100001110010010001011000000010101101110010110100000000010111111101100100010001000111111100111011101000010101111101000000000011001000110101111011001011001000101101011111100001101000100000000000010001010100000111101011101111010101010000000011110011010110110000000010111000100001011000010010011000100000100110101000011000100100000000000001111101111110100000010101111111100001001001110001111111100000000010111001011011001100001101010001000100111111100101011011000000000000000000011111101001100110000110100010001111001111101100111010110000000010101101101010110010011111101010000100100010000110001011010000000000010100011111001110010000010000111111110110001110111111011111010000000000001000000111110111001100111011011010100110100111011110010000000000000110111001011011100010111100000000010010011111010100110111110000000001110000110101101001100001001000111010011100110001111001100110000000001100011100110101100100101001010001100110101000111111101111000000000011101100100001000100011101010100111110101110010101001001010100000000011111110000111010111100001011011101000110100100101000101001010000000010111101100010010110000011101011011101101101101100001011000110000000010101111001111111101011010100000001000100101001010110110101000000000000011101100001001011001110010110111100101110010100000011001100000000010001111010011100000100100110111011011010111011010111110001110000000010000001111101010111111110001110110001011111101110000100000110000000000100010110000000101000111001011000011100011001011111011111110000000010110101101100011101011100001111101011111111000110010000011000000000000111111100010001000110100000001101111110101011011110011010110000000011101000111010011011110100001010110011111101010001001000000010000000011110011110100001110111110011111101110100001001111011111101110000000000000000100110000001011111101000100101001101100110001000110100000000011111110011101111000110110011010110001110011111101111010111110000000010000010010101100101110011001000111101111010111001011000100110000000010111010100001010110001101111111101101000111010111111111101000000000010111010101001111101101011110000100011100011110011011111101010000000010111010100101010001000010100001001101000100001110100010001010000000010000010001010111111111001100110111100101011111000010110000010000000011111110001011000110010001101010000101110110000110101000011110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_encode_utf8_bom() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("https://en.wikipedia.org/wiki/🍕", ECCLevel.L, true, true, QRCodeGenerator.EciMode.Utf8); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110010001101010101111111000000001000001011011000110000100000100000000101110100111010101111010111010000000010111010110100100010101011101000000001011101000101111000010101110100000000100000101010000111000010000010000000011111110101010101010101111111000000000000000000001010101110000000000000000111110111110101010100101010100000000000100000110000101000001100101000000000001001001011000011010000111100000000100010001111000001111110111010000000010110111010100011100100101111000000000001010001101101001000010100100000000100001101110011001010000001010000000001011001100011001111111010111000000000010001010101011110010100000100000000100100010000000000010110010000000000010110110010110000101010101100000000001001100100010010100111101101100000000101010110011000111101111100100000000000000000111011110011100011010000000001111111011100110010010101110000000000100000100100110010101000110110000000010111010110010111101111110011000000001011101010100000100010110100000000000101110101001100111110110111100000000010000010111100101111100100001000000001111111011110001110100111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_byte() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("äöü", ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111001011011111110000000010000010011100100000100000000101110101101101011101000000001011101001010010111010000000010111010001010101110100000000100000100000101000001000000001111111010101011111110000000000000000110110000000000000000111011111111011000100000000001001110001100010000010000000010011110001010001001000000000110011010000001000110000000001110001111001010110110000000000000000111101010011100000000111111101111011100110000000001000001010011101110010000000010111010110101110010100000000101110100110001000110000000001011101011001000100010000000010000010100000100011000000000111111101110101010111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void can_generate_from_bytes() - { - byte[] test_data = { 49, 50, 51, 65, 66, 67 }; //123ABC - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode(test_data, ECCLevel.L); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011001011111110000000010000010010010100000100000000101110101010101011101000000001011101010010010111010000000010111010111000101110100000000100000100000001000001000000001111111010101011111110000000000000000011000000000000000000111100101010010011101000000001011100001001001001110000000010101011111011111110100000000000101000000110000000000000001011001001010100110000000000000000000110001000101000000000111111100110011011110000000001000001001111110111010000000010111010011100100101100000000101110101110010010010000000001011101011010100011000000000010000010110110101000100000000111111101011100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_utf8() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("https://en.wikipedia.org/wiki/🍕", ECCLevel.L, true, false, QRCodeGenerator.EciMode.Utf8); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110101011010011101111111000000001000001001111100001110100000100000000101110101110000011000010111010000000010111010111010111100101011101000000001011101010011010111010101110100000000100000100011010001110010000010000000011111110101010101010101111111000000000000000000101000011100000000000000000111100101011110101011100111010000000001011000101011111010011101010000000001010011101111101001111011101000000000111011011110000010001100000100000000000000010011010101100000000000000000001100110101011011111001101110000000000000011100001010101010110101000000000000111001011100110111111110011000000001110101011001011001000100011000000000000101010100001010111111000000000000010111010101001111100000001110000000000010110100010111111100100010100000000011101111010011101111111101010000000000000000110000001000100010010000000001111111001100011001010101101000000000100000100111111111011000111000000000010111010010100011010111110111000000001011101010110100011100101011000000000101110101100101111100101111010000000010000010111011001111000001101000000001111111011110000100000110101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void trim_leading_zeros_works() - { - var gen = new QRCodeGenerator(); - var qrData = gen.CreateQrCode("this is a test", ECCLevel.M); - var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); - result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111001101011111110000000010000010010000100000100000000101110101101101011101000000001011101010001010111010000000010111010101010101110100000000100000101010101000001000000001111111010101011111110000000000000000110010000000000000000101111100011101111100000000001110100011110001100010000000001100011010110010011000000000100111000011010011100000000001001011001101011000100000000000000000100100001001100000000111111100111110001110000000001000001010011000011010000000010111010101110111101100000000101110101000000110100000000001011101011111000010000000000010000010010011010010000000000111111101101111100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); - } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_utf8_bom() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("https://en.wikipedia.org/wiki/🍕", ECCLevel.L, true, true, QRCodeGenerator.EciMode.Utf8); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011111110010001101010101111111000000001000001011011000110000100000100000000101110100111010101111010111010000000010111010110100100010101011101000000001011101000101111000010101110100000000100000101010000111000010000010000000011111110101010101010101111111000000000000000000001010101110000000000000000111110111110101010100101010100000000000100000110000101000001100101000000000001001001011000011010000111100000000100010001111000001111110111010000000010110111010100011100100101111000000000001010001101101001000010100100000000100001101110011001010000001010000000001011001100011001111111010111000000000010001010101011110010100000100000000100100010000000000010110010000000000010110110010110000101010101100000000001001100100010010100111101101100000000101010110011000111101111100100000000000000000111011110011100011010000000001111111011100110010010101110000000000100000100100110010101000110110000000010111010110010111101111110011000000001011101010100000100010110100000000000101110101001100111110110111100000000010000010111100101111100100001000000001111111011110001110100111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - [Fact] - [Category("QRGenerator/TextEncoding")] - public void isValidIso_works() - { - // see private method: QRCodeGenerator.IsValidISO + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_generate_from_bytes() + { + byte[] test_data = { 49, 50, 51, 65, 66, 67 }; //123ABC + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode(test_data, ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011001011111110000000010000010010010100000100000000101110101010101011101000000001011101010010010111010000000010111010111000101110100000000100000100000001000001000000001111111010101011111110000000000000000011000000000000000000111100101010010011101000000001011100001001001001110000000010101011111011111110100000000000101000000110000000000000001011001001010100110000000000000000000110001000101000000000111111100110011011110000000001000001001111110111010000000010111010011100100101100000000101110101110010010010000000001011101011010100011000000000010000010110110101000100000000111111101011100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - Encoding _iso88591ExceptionFallback = Encoding.GetEncoding(28591, new EncoderExceptionFallback(), new DecoderExceptionFallback()); // ISO-8859-1 + [Fact] + [Category("QRGenerator/TextEncoding")] + public void trim_leading_zeros_works() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("this is a test", ECCLevel.M); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111001101011111110000000010000010010000100000100000000101110101101101011101000000001011101010001010111010000000010111010101010101110100000000100000101010101000001000000001111111010101011111110000000000000000110010000000000000000101111100011101111100000000001110100011110001100010000000001100011010110010011000000000100111000011010011100000000001001011001101011000100000000000000000100100001001100000000111111100111110001110000000001000001010011000011010000000010111010101110111101100000000101110101000000110100000000001011101011111000010000000000010000010010011010010000000000111111101101111100010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } - IsValidISO("abc").ShouldBeTrue(); - IsValidISO("äöü").ShouldBeTrue(); - IsValidISO("🍕").ShouldBeFalse(); + [Fact] + [Category("QRGenerator/TextEncoding")] + public void isValidIso_works() + { + // see private method: QRCodeGenerator.IsValidISO - bool IsValidISO(string input) - { - try - { - _ = _iso88591ExceptionFallback.GetByteCount(input); - return true; - } - catch (EncoderFallbackException) - { - return false; - } - } - } + var _iso88591ExceptionFallback = Encoding.GetEncoding(28591, new EncoderExceptionFallback(), new DecoderExceptionFallback()); // ISO-8859-1 - [Fact] - [Category("QRGenerator/EccLevel")] - public void ecc_level_from_payload_works() + IsValidISO("abc").ShouldBeTrue(); + IsValidISO("äöü").ShouldBeTrue(); + IsValidISO("🍕").ShouldBeFalse(); + + bool IsValidISO(string input) { - var stringValue = "this is a test"; - - // set up baselines - var expectedL = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.L)); - var expectedM = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.M)); - var expectedQ = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.Q)); - var expectedH = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.H)); - - // ensure that the baselines are different from each other - expectedL.ShouldNotBe(expectedM); - expectedL.ShouldNotBe(expectedQ); - expectedL.ShouldNotBe(expectedH); - expectedM.ShouldNotBe(expectedQ); - expectedM.ShouldNotBe(expectedH); - expectedQ.ShouldNotBe(expectedH); - - // validate that any ECC level can be used when the payload specifies a default ECC level - var payloadDefault = new SamplePayload(stringValue, QRCodeGenerator.ECCLevel.Default); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault)).ShouldBe(expectedM); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Default)).ShouldBe(expectedM); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.L)).ShouldBe(expectedL); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.M)).ShouldBe(expectedM); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Q)).ShouldBe(expectedQ); - Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.H)).ShouldBe(expectedH); - - // validate that the ECC level specified in the payload is used when default is specified, - // or checks that the selected ECC level matches the payload ECC level, throwing an exception otherwise - Verify(QRCodeGenerator.ECCLevel.L, expectedL); - Verify(QRCodeGenerator.ECCLevel.M, expectedM); - Verify(QRCodeGenerator.ECCLevel.Q, expectedQ); - Verify(QRCodeGenerator.ECCLevel.H, expectedH); - - - void Verify(QRCodeGenerator.ECCLevel eccLevel, string expected) + try { - var payload = new SamplePayload(stringValue, eccLevel); - Encode(QRCodeGenerator.GenerateQrCode(payload)).ShouldBe(expected); - foreach (var ecc in Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast()) - { - if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default) - Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected); - else - Should.Throw(() => Encode(QRCodeGenerator.GenerateQrCode(payload, ecc))); - } + _ = _iso88591ExceptionFallback.GetByteCount(input); + return true; } - - string Encode(QRCodeData qrData) + catch (EncoderFallbackException) { - return string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + return false; } } + } - private class SamplePayload : PayloadGenerator.Payload + [Fact] + [Category("QRGenerator/EccLevel")] + public void ecc_level_from_payload_works() + { + var stringValue = "this is a test"; + + // set up baselines + var expectedL = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.L)); + var expectedM = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.M)); + var expectedQ = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.Q)); + var expectedH = Encode(QRCodeGenerator.GenerateQrCode(stringValue, QRCodeGenerator.ECCLevel.H)); + + // ensure that the baselines are different from each other + expectedL.ShouldNotBe(expectedM); + expectedL.ShouldNotBe(expectedQ); + expectedL.ShouldNotBe(expectedH); + expectedM.ShouldNotBe(expectedQ); + expectedM.ShouldNotBe(expectedH); + expectedQ.ShouldNotBe(expectedH); + + // validate that any ECC level can be used when the payload specifies a default ECC level + var payloadDefault = new SamplePayload(stringValue, QRCodeGenerator.ECCLevel.Default); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault)).ShouldBe(expectedM); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Default)).ShouldBe(expectedM); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.L)).ShouldBe(expectedL); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.M)).ShouldBe(expectedM); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.Q)).ShouldBe(expectedQ); + Encode(QRCodeGenerator.GenerateQrCode(payloadDefault, QRCodeGenerator.ECCLevel.H)).ShouldBe(expectedH); + + // validate that the ECC level specified in the payload is used when default is specified, + // or checks that the selected ECC level matches the payload ECC level, throwing an exception otherwise + Verify(QRCodeGenerator.ECCLevel.L, expectedL); + Verify(QRCodeGenerator.ECCLevel.M, expectedM); + Verify(QRCodeGenerator.ECCLevel.Q, expectedQ); + Verify(QRCodeGenerator.ECCLevel.H, expectedH); + + + void Verify(QRCodeGenerator.ECCLevel eccLevel, string expected) { - private string _data; - private QRCodeGenerator.ECCLevel _eccLevel; - - public SamplePayload(string data, QRCodeGenerator.ECCLevel eccLevel) + var payload = new SamplePayload(stringValue, eccLevel); + Encode(QRCodeGenerator.GenerateQrCode(payload)).ShouldBe(expected); + foreach (var ecc in Enum.GetValues(typeof(QRCodeGenerator.ECCLevel)).Cast()) { - _data = data; - _eccLevel = eccLevel; + if (ecc == eccLevel || ecc == QRCodeGenerator.ECCLevel.Default) + Encode(QRCodeGenerator.GenerateQrCode(payload, ecc)).ShouldBe(expected); + else + Should.Throw(() => Encode(QRCodeGenerator.GenerateQrCode(payload, ecc))); } + } + + string Encode(QRCodeData qrData) => string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + } - public override QRCodeGenerator.ECCLevel EccLevel => _eccLevel; + private class SamplePayload : PayloadGenerator.Payload + { + private readonly string _data; + private readonly QRCodeGenerator.ECCLevel _eccLevel; - public override string ToString() => _data; + public SamplePayload(string data, QRCodeGenerator.ECCLevel eccLevel) + { + _data = data; + _eccLevel = eccLevel; } + + public override QRCodeGenerator.ECCLevel EccLevel => _eccLevel; + + public override string ToString() => _data; } +} - public static class ExtensionMethods +public static class ExtensionMethods +{ + public static string ToBitString(this BitArray bits) { - public static string ToBitString(this BitArray bits) + var sb = new StringBuilder(); + int bitLength = bits.Length; + for (int i = 0; i < bitLength; i++) { - var sb = new StringBuilder(); - int bitLength = bits.Length; - for (int i = 0; i < bitLength; i++) - { - char c = bits[i] ? '1' : '0'; - sb.Append(c); - } - - return sb.ToString(); + char c = bits[i] ? '1' : '0'; + sb.Append(c); } + + return sb.ToString(); } } diff --git a/QRCoderTests/SvgQRCodeRendererTests.cs b/QRCoderTests/SvgQRCodeRendererTests.cs index 9b296272..6b91452e 100644 --- a/QRCoderTests/SvgQRCodeRendererTests.cs +++ b/QRCoderTests/SvgQRCodeRendererTests.cs @@ -1,231 +1,230 @@ -#if !NETCOREAPP1_1 +#if !NETCOREAPP1_1 using System; -using Xunit; -using QRCoder; -using Shouldly; -using QRCoderTests.Helpers.XUnitExtenstions; +using System.Drawing; using System.IO; +using QRCoder; using QRCoderTests.Helpers; -using System.Drawing; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; -namespace QRCoderTests +namespace QRCoderTests; + + +public class SvgQRCodeRendererTests { + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_simple() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); + var svg = new SvgQRCode(data).GetGraphic(5); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("5c251275a435a9aed7e591eb9c2e9949"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("1baa8c6ac3bd8c1eabcd2c5422dd9f78"); + } - public class SvgQRCodeRendererTests + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_viewbox_mode() { - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_simple() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L); - var svg = new SvgQRCode(data).GetGraphic(5); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("5c251275a435a9aed7e591eb9c2e9949"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("1baa8c6ac3bd8c1eabcd2c5422dd9f78"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_viewbox_mode() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var svg = new SvgQRCode(data).GetGraphic(new Size(128,128)); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("56719c7db39937c74377855a5dc4af0a"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_viewbox_mode_viewboxattr() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var svg = new SvgQRCode(data).GetGraphic(new Size(128, 128), sizingMode: SvgQRCode.SizingMode.ViewBoxAttribute); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("788afdb693b0b71eed344e495c180b60"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_without_quietzones() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White, false); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("2a582427d86b51504c08ebcbcf0472bd"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_without_quietzones_hex() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var svg = new SvgQRCode(data).GetGraphic(10, "#000000", "#ffffff", false); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("4ab0417cc6127e347ca1b2322c49ed7d"); - } + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var svg = new SvgQRCode(data).GetGraphic(new Size(128, 128)); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("56719c7db39937c74377855a5dc4af0a"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_viewbox_mode_viewboxattr() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var svg = new SvgQRCode(data).GetGraphic(new Size(128, 128), sizingMode: SvgQRCode.SizingMode.ViewBoxAttribute); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("788afdb693b0b71eed344e495c180b60"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_without_quietzones() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var svg = new SvgQRCode(data).GetGraphic(10, Color.Red, Color.White, false); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("2a582427d86b51504c08ebcbcf0472bd"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_without_quietzones_hex() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var svg = new SvgQRCode(data).GetGraphic(10, "#000000", "#ffffff", false); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("4ab0417cc6127e347ca1b2322c49ed7d"); + } #if SYSTEM_DRAWING && !NET5_0_OR_GREATER // .NET 5+ does not encode PNG images in a deterministic way, so the hash may be different across different runs - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_png_logo_bitmap() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("78e02e8ba415f15817d5ed88c4afca31"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_png_logo_bitmap_without_background() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15, false); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("f221b2baecc2883f8e8ae54f12ba701b"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.Black, Color.White, drawQuietZones: false, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("8b4d114136c7fd26e0b34e5a15daac3b"); - } + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_png_logo_bitmap() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("78e02e8ba415f15817d5ed88c4afca31"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_png_logo_bitmap_without_background() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15, false); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("f221b2baecc2883f8e8ae54f12ba701b"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_png_logo_bitmap_without_quietzones() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var logoBitmap = (Bitmap)Image.FromFile(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.Black, Color.White, drawQuietZones: false, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("8b4d114136c7fd26e0b34e5a15daac3b"); + } #endif - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_png_logo_bytearray() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 - var logoBitmap = System.IO.File.ReadAllBytes(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); - var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("7d53f25af04e52b20550deb2e3589e96"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_svg_logo_embedded() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361 - var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg"); - var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20); - logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.SVG); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("855eb988d3af035abd273ed1629aa952"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_with_svg_logo_image_tag() - { - //Create QR code - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - - //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361 - var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg"); - var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20, iconEmbedded: false); - - var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("bd442ea77d45a41a4f490b8d41591e04"); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_instantate_parameterless() - { - var svgCode = new SvgQRCode(); - svgCode.ShouldNotBeNull(); - svgCode.ShouldBeOfType(); - } - - [Fact] - [Category("QRRenderer/SvgQRCode")] - public void can_render_svg_qrcode_from_helper() - { - //Create QR code - var svg = SvgQRCodeHelper.GetQRCode("A", 2, "#000000", "#ffffff", QRCodeGenerator.ECCLevel.Q); - - var result = HelperFunctions.StringToHash(svg); - result.ShouldBe("f5ec37aa9fb207e3701cc0d86c4a357d"); - } + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_png_logo_bytearray() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909346 + var logoBitmap = System.IO.File.ReadAllBytes(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_software engineer_2909346.png"); + var logoObj = new SvgQRCode.SvgLogo(iconRasterized: logoBitmap, 15); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.PNG); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("7d53f25af04e52b20550deb2e3589e96"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_svg_logo_embedded() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361 + var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg"); + var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20); + logoObj.GetMediaType().ShouldBe(SvgQRCode.SvgLogo.MediaType.SVG); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("855eb988d3af035abd273ed1629aa952"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_with_svg_logo_image_tag() + { + //Create QR code + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + + //Used logo is licensed under public domain. Ref.: https://thenounproject.com/Iconathon1/collection/redefining-women/?i=2909361 + var logoSvg = File.ReadAllText(HelperFunctions.GetAssemblyPath() + "\\assets\\noun_Scientist_2909361.svg"); + var logoObj = new SvgQRCode.SvgLogo(logoSvg, 20, iconEmbedded: false); + + var svg = new SvgQRCode(data).GetGraphic(10, Color.DarkGray, Color.White, logo: logoObj); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("bd442ea77d45a41a4f490b8d41591e04"); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_instantate_parameterless() + { + var svgCode = new SvgQRCode(); + svgCode.ShouldNotBeNull(); + svgCode.ShouldBeOfType(); + } + + [Fact] + [Category("QRRenderer/SvgQRCode")] + public void can_render_svg_qrcode_from_helper() + { + //Create QR code + var svg = SvgQRCodeHelper.GetQRCode("A", 2, "#000000", "#ffffff", QRCodeGenerator.ECCLevel.Q); + + var result = HelperFunctions.StringToHash(svg); + result.ShouldBe("f5ec37aa9fb207e3701cc0d86c4a357d"); } } #endif diff --git a/QRCoderTests/XamlQRCodeRendererTests.cs b/QRCoderTests/XamlQRCodeRendererTests.cs index 1e5d27f4..eb1eda2a 100644 --- a/QRCoderTests/XamlQRCodeRendererTests.cs +++ b/QRCoderTests/XamlQRCodeRendererTests.cs @@ -1,52 +1,51 @@ -#if TEST_XAML -using Xunit; +#if TEST_XAML using QRCoder; using QRCoder.Xaml; -using Shouldly; -using QRCoderTests.Helpers.XUnitExtenstions; using QRCoderTests.Helpers; +using QRCoderTests.Helpers.XUnitExtenstions; +using Shouldly; +using Xunit; + +namespace QRCoderTests; + -namespace QRCoderTests +public class XamlQRCodeRendererTests { - public class XamlQRCodeRendererTests + [Fact] + [Category("QRRenderer/XamlQRCode")] + public void can_create_xaml_qrcode_standard_graphic() + { + var gen = new QRCodeGenerator(); + var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); + var xCode = new XamlQRCode(data).GetGraphic(10); + + var bmp = HelperFunctions.BitmapSourceToBitmap(xCode); + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); + } + + + [Fact] + [Category("QRRenderer/XamlQRCode")] + public void can_instantate_qrcode_parameterless() + { + var svgCode = new XamlQRCode(); + svgCode.ShouldNotBeNull(); + svgCode.ShouldBeOfType(); + } + + /* + [Fact] + [Category("QRRenderer/XamlQRCode")] + public void can_render_qrcode_from_helper() { + //Create QR code + var bmp = QRCodeHelper.GetQRCode("This is a quick test! 123#?", 10, Color.Black, Color.White, QRCodeGenerator.ECCLevel.H); - [Fact] - [Category("QRRenderer/XamlQRCode")] - public void can_create_xaml_qrcode_standard_graphic() - { - var gen = new QRCodeGenerator(); - var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H); - var xCode = new XamlQRCode(data).GetGraphic(10); - - var bmp = HelperFunctions.BitmapSourceToBitmap(xCode); - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("f2ed5073bd42dc012e442c0f750e9dae"); - } - - - [Fact] - [Category("QRRenderer/XamlQRCode")] - public void can_instantate_qrcode_parameterless() - { - var svgCode = new XamlQRCode(); - svgCode.ShouldNotBeNull(); - svgCode.ShouldBeOfType(); - } - - /* - [Fact] - [Category("QRRenderer/XamlQRCode")] - public void can_render_qrcode_from_helper() - { - //Create QR code - var bmp = QRCodeHelper.GetQRCode("This is a quick test! 123#?", 10, Color.Black, Color.White, QRCodeGenerator.ECCLevel.H); - - var result = HelperFunctions.BitmapToHash(bmp); - result.ShouldBe("e8c61b8f0455924fe08ba68686d0d296"); - } - */ + var result = HelperFunctions.BitmapToHash(bmp); + result.ShouldBe("e8c61b8f0455924fe08ba68686d0d296"); } + */ } -#endif \ No newline at end of file +#endif diff --git a/QRCoderTrimAnalysis/Program.cs b/QRCoderTrimAnalysis/Program.cs index 3751555c..39946ea1 100644 --- a/QRCoderTrimAnalysis/Program.cs +++ b/QRCoderTrimAnalysis/Program.cs @@ -1,2 +1,2 @@ -// See https://aka.ms/new-console-template for more information +// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!"); diff --git a/QRCoderTrimAnalysis/QRCoderTrimAnalysis.csproj b/QRCoderTrimAnalysis/QRCoderTrimAnalysis.csproj index 0f65205f..32273422 100644 --- a/QRCoderTrimAnalysis/QRCoderTrimAnalysis.csproj +++ b/QRCoderTrimAnalysis/QRCoderTrimAnalysis.csproj @@ -1,4 +1,4 @@ - + @@ -8,14 +8,15 @@ net8.0 enable enable - true - true - false + true + true + + false - +