diff --git a/QRCoder/Attributes/InterpolatedStringHandlerAttributes.cs b/QRCoder/Attributes/InterpolatedStringHandlerAttributes.cs
new file mode 100644
index 00000000..5a91b22f
--- /dev/null
+++ b/QRCoder/Attributes/InterpolatedStringHandlerAttributes.cs
@@ -0,0 +1,80 @@
+#if !NET6_0_OR_GREATER
+namespace System.Runtime.CompilerServices;
+
+///
+/// Indicates the attributed type is an interpolated string handler.
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
+internal sealed class InterpolatedStringHandlerAttribute : Attribute
+{
+}
+
+///
+/// Indicates which arguments an interpolated string handler passes through to the underlying handler constructor.
+///
+[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
+internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the argument that should be passed to the handler.
+ public InterpolatedStringHandlerArgumentAttribute(string argument)
+ {
+ Arguments = new[] { argument };
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the first argument that should be passed to the handler.
+ /// The name of the second argument that should be passed to the handler.
+ public InterpolatedStringHandlerArgumentAttribute(string argument1, string argument2)
+ {
+ Arguments = new[] { argument1, argument2 };
+ }
+
+ ///
+ /// Gets the arguments that should be passed to the handler.
+ ///
+ public string[] Arguments { get; }
+}
+
+///
+/// Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
+///
+[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+internal sealed class CompilerFeatureRequiredAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the compiler feature.
+ public CompilerFeatureRequiredAttribute(string featureName)
+ {
+ FeatureName = featureName;
+ }
+
+ ///
+ /// Gets the name of the compiler feature.
+ ///
+ public string FeatureName { get; }
+
+ ///
+ /// Gets a value that indicates whether the compiler can choose to allow access if it does not understand .
+ ///
+ public bool IsOptional { get; set; }
+
+ ///
+ /// The feature name used for ref structs.
+ ///
+#pragma warning disable IDE1006 // Must match the BCL constant names
+ public const string RefStructs = nameof(RefStructs);
+
+ ///
+ /// The feature name used for required members.
+ ///
+ public const string RequiredMembers = nameof(RequiredMembers);
+#pragma warning restore IDE1006
+}
+#endif
diff --git a/QRCoder/Extensions/StringExtensions.cs b/QRCoder/Extensions/StringExtensions.cs
index 6ad02de9..8857fb0c 100644
--- a/QRCoder/Extensions/StringExtensions.cs
+++ b/QRCoder/Extensions/StringExtensions.cs
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
namespace QRCoder;
@@ -67,6 +68,21 @@ internal static string ToString(this char c, CultureInfo _)
=> c.ToString();
#endif
+ ///
+ /// Appends an interpolated string using invariant culture formatting.
+ /// On .NET 6+ appends via the invariant-culture interpolated-string handler overload (no extra string allocation).
+ /// Must be a static method (not an extension) so the handler can reference the parameter (CS8944).
+ ///
+#if NET6_0_OR_GREATER
+ internal static void AppendInvariant(
+ StringBuilder sb,
+ [InterpolatedStringHandlerArgument(nameof(sb))] ref StringBuilder.AppendInterpolatedStringHandler handler)
+ => sb.Append(CultureInfo.InvariantCulture, ref handler);
+#else
+ internal static void AppendInvariant(StringBuilder sb, string value)
+ => sb.Append(value);
+#endif
+
///
/// Appends an integer value to the StringBuilder using invariant culture formatting.
///
@@ -75,7 +91,7 @@ internal static string ToString(this char c, CultureInfo _)
internal static void AppendInvariant(this StringBuilder sb, int num)
{
#if NET6_0_OR_GREATER
- sb.Append(CultureInfo.InvariantCulture, $"{num}");
+ AppendInvariant(sb, $"{num}");
#else
#if HAS_SPAN
Span buffer = stackalloc char[16];
@@ -97,7 +113,7 @@ internal static void AppendInvariant(this StringBuilder sb, int num)
internal static void AppendInvariant(this StringBuilder sb, float num)
{
#if NET6_0_OR_GREATER
- sb.Append(CultureInfo.InvariantCulture, $"{num:G7}");
+ AppendInvariant(sb, $"{num:G7}");
#else
#if HAS_SPAN
Span buffer = stackalloc char[16];
diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs
index 1bb15d81..71681a19 100644
--- a/QRCoder/PayloadGenerator.cs
+++ b/QRCoder/PayloadGenerator.cs
@@ -24,7 +24,16 @@ internal static bool IsValidIban(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(CultureInfo.InvariantCulture) : c.ToString(CultureInfo.InvariantCulture)));
+ var sumChars = $"{ibanCleared.Substring(4)}{ibanCleared.Substring(0, 4)}".ToCharArray();
+ var sumBuilder = new StringBuilder(sumChars.Length * 2);
+ foreach (var c in sumChars)
+ {
+ if (char.IsLetter(c))
+ sumBuilder.Append((c - 55).ToString(CultureInfo.InvariantCulture));
+ else
+ sumBuilder.Append(c);
+ }
+ var sum = sumBuilder.ToString();
int m = 0;
for (int i = 0; i < (int)Math.Ceiling((sum.Length - 2) / 7d); i++)
{
@@ -33,7 +42,7 @@ internal static bool IsValidIban(string iban)
#if NET5_0_OR_GREATER
var n = string.Concat(i == 0 ? "" : m.ToString(CultureInfo.InvariantCulture), sum.AsSpan(start, Math.Min(9 - offset, sum.Length - start)));
#else
- var n = (i == 0 ? "" : m.ToString(CultureInfo.InvariantCulture)) + sum.Substring(start, Math.Min(9 - offset, sum.Length - start));
+ var n = $"{(i == 0 ? "" : m.ToString(CultureInfo.InvariantCulture))}{sum.Substring(start, Math.Min(9 - offset, sum.Length - start))}";
#endif
if (!int.TryParse(n, NumberStyles.Any, CultureInfo.InvariantCulture, out m))
break;
@@ -112,7 +121,7 @@ private static string EscapeInput(string inp, bool simple = false)
}
foreach (var c in forbiddenChars)
{
- inp = inp.Replace(c.ToString(), "\\" + c);
+ inp = inp.Replace(c.ToString(), $"\\{c}");
}
return inp;
}
diff --git a/QRCoder/PayloadGenerator/BezahlCode.cs b/QRCoder/PayloadGenerator/BezahlCode.cs
index ba617fb5..63d398ad 100644
--- a/QRCoder/PayloadGenerator/BezahlCode.cs
+++ b/QRCoder/PayloadGenerator/BezahlCode.cs
@@ -148,7 +148,7 @@ public BezahlCode(AuthorityType authority, string name, string account, string b
_name = name;
//Limit reason length depending on payment type
- //140 chars for SEPA payments and 27 chars for others
+ //140 chars for SEPA payments and 27 chars for others
var reasonLength = authority == AuthorityType.periodicsinglepaymentsepa || authority == AuthorityType.singledirectdebitsepa || authority == AuthorityType.singlepaymentsepa || (authority == AuthorityType.contact_v2 && newWayFilled) ? 140 : 27;
if (reason.Length > reasonLength)
throw new BezahlCodeException($"Reasons texts have to be shorter than {reasonLength + 1} chars.");
@@ -246,9 +246,8 @@ public BezahlCode(AuthorityType authority, string name, string account, string b
///
public override string ToString()
{
- var bezahlCodePayload = $"bank://{_authority}?";
-
- bezahlCodePayload += $"name={Uri.EscapeDataString(_name)}&";
+ var bezahlCodePayload = new StringBuilder($"bank://{_authority}?");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"name={Uri.EscapeDataString(_name)}&");
if (_authority != AuthorityType.contact && _authority != AuthorityType.contact_v2)
{
@@ -257,44 +256,44 @@ public override string ToString()
if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.singledirectdebit || _authority == AuthorityType.singlepayment)
#pragma warning restore CS0618
{
- bezahlCodePayload += $"account={_account}&";
- bezahlCodePayload += $"bnc={_bnc}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"account={_account}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"bnc={_bnc}&");
if (_postingKey > 0)
- bezahlCodePayload += $"postingkey={_postingKey}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"postingkey={_postingKey}&");
}
else
{
- bezahlCodePayload += $"iban={_iban}&";
- bezahlCodePayload += $"bic={_bic}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"iban={_iban}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"bic={_bic}&");
if (!string.IsNullOrEmpty(_sepaReference))
- bezahlCodePayload += $"separeference={Uri.EscapeDataString(_sepaReference)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"separeference={Uri.EscapeDataString(_sepaReference)}&");
if (_authority == AuthorityType.singledirectdebitsepa)
{
if (!string.IsNullOrEmpty(_creditorId))
- bezahlCodePayload += $"creditorid={Uri.EscapeDataString(_creditorId)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"creditorid={Uri.EscapeDataString(_creditorId)}&");
if (!string.IsNullOrEmpty(_mandateId))
- bezahlCodePayload += $"mandateid={Uri.EscapeDataString(_mandateId)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"mandateid={Uri.EscapeDataString(_mandateId)}&");
if (_dateOfSignature != DateTime.MinValue)
- bezahlCodePayload += $"dateofsignature={_dateOfSignature.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"dateofsignature={_dateOfSignature.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&");
}
}
- bezahlCodePayload += string.Format(CultureInfo.InvariantCulture, "amount={0:0.00}&", _amount).Replace(".", ",");
+ bezahlCodePayload.Append(string.Format(CultureInfo.InvariantCulture, "amount={0:0.00}&", _amount).Replace(".", ","));
if (!string.IsNullOrEmpty(_reason))
- bezahlCodePayload += $"reason={Uri.EscapeDataString(_reason)}&";
- bezahlCodePayload += $"currency={_currency}&";
- bezahlCodePayload += $"executiondate={_executionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"reason={Uri.EscapeDataString(_reason)}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"currency={_currency}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"executiondate={_executionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&");
#pragma warning disable CS0618
if (_authority == AuthorityType.periodicsinglepayment || _authority == AuthorityType.periodicsinglepaymentsepa)
{
- bezahlCodePayload += $"periodictimeunit={_periodicTimeunit}&";
- bezahlCodePayload += $"periodictimeunitrotation={_periodicTimeunitRotation}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"periodictimeunit={_periodicTimeunit}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"periodictimeunitrotation={_periodicTimeunitRotation}&");
if (_periodicFirstExecutionDate != DateTime.MinValue)
- bezahlCodePayload += $"periodicfirstexecutiondate={_periodicFirstExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"periodicfirstexecutiondate={_periodicFirstExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&");
if (_periodicLastExecutionDate != DateTime.MinValue)
- bezahlCodePayload += $"periodiclastexecutiondate={_periodicLastExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"periodiclastexecutiondate={_periodicLastExecutionDate.ToString("ddMMyyyy", CultureInfo.InvariantCulture)}&");
}
#pragma warning restore CS0618
}
@@ -303,28 +302,29 @@ public override string ToString()
//Handle what is same for all contacts
if (_authority == AuthorityType.contact)
{
- bezahlCodePayload += $"account={_account}&";
- bezahlCodePayload += $"bnc={_bnc}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"account={_account}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"bnc={_bnc}&");
}
else if (_authority == AuthorityType.contact_v2)
{
if (!string.IsNullOrEmpty(_account) && !string.IsNullOrEmpty(_bnc))
{
- bezahlCodePayload += $"account={_account}&";
- bezahlCodePayload += $"bnc={_bnc}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"account={_account}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"bnc={_bnc}&");
}
else
{
- bezahlCodePayload += $"iban={_iban}&";
- bezahlCodePayload += $"bic={_bic}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"iban={_iban}&");
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"bic={_bic}&");
}
}
if (!string.IsNullOrEmpty(_reason))
- bezahlCodePayload += $"reason={Uri.EscapeDataString(_reason)}&";
+ StringExtensions.AppendInvariant(bezahlCodePayload,$"reason={Uri.EscapeDataString(_reason)}&");
}
- return bezahlCodePayload.Trim('&');
+ string result = bezahlCodePayload.ToString();
+ return result.TrimEnd('&');
}
///
diff --git a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs
index d9f05245..4d1ea0d5 100644
--- a/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs
+++ b/QRCoder/PayloadGenerator/BitcoinLikeCryptoCurrencyAddress.cs
@@ -55,10 +55,10 @@ public override string ToString()
if (queryValues.Any(keyPair => !string.IsNullOrEmpty(keyPair.Value)))
{
- query = "?" + string.Join("&", queryValues
+ query = $"?{string.Join("&", queryValues
.Where(keyPair => !string.IsNullOrEmpty(keyPair.Value))
.Select(keyPair => $"{keyPair.Key}={keyPair.Value}")
- .ToArray());
+ .ToArray())}";
}
return $"{Enum.GetName(typeof(BitcoinLikeCryptoCurrencyType), _currencyType)!.ToLowerInvariant()}:{_address}{query}";
diff --git a/QRCoder/PayloadGenerator/ContactData.cs b/QRCoder/PayloadGenerator/ContactData.cs
index e2d3e111..89a420d7 100644
--- a/QRCoder/PayloadGenerator/ContactData.cs
+++ b/QRCoder/PayloadGenerator/ContactData.cs
@@ -114,139 +114,127 @@ public ContactData(ContactOutputType outputType, string firstname, string lastna
/// A string representation of the contact data in the specified format.
public override string ToString()
{
- string payload = string.Empty;
+ var payload = new StringBuilder();
if (_outputType == ContactOutputType.MeCard)
{
- payload += "MECARD+\r\n";
+ payload.Append("MECARD+\r\n");
if (!string.IsNullOrEmpty(_firstname) && !string.IsNullOrEmpty(_lastname))
- payload += $"N:{_lastname}, {_firstname}\r\n";
+ StringExtensions.AppendInvariant(payload,$"N:{_lastname}, {_firstname}\r\n");
else if (!string.IsNullOrEmpty(_firstname) || !string.IsNullOrEmpty(_lastname))
- payload += $"N:{_firstname}{_lastname}\r\n";
+ StringExtensions.AppendInvariant(payload,$"N:{_firstname}{_lastname}\r\n");
if (!string.IsNullOrEmpty(_org))
- payload += $"ORG:{_org}\r\n";
+ StringExtensions.AppendInvariant(payload,$"ORG:{_org}\r\n");
if (!string.IsNullOrEmpty(_orgTitle))
- payload += $"TITLE:{_orgTitle}\r\n";
+ StringExtensions.AppendInvariant(payload,$"TITLE:{_orgTitle}\r\n");
if (!string.IsNullOrEmpty(_phone))
- payload += $"TEL:{_phone}\r\n";
+ StringExtensions.AppendInvariant(payload,$"TEL:{_phone}\r\n");
if (!string.IsNullOrEmpty(_mobilePhone))
- payload += $"TEL:{_mobilePhone}\r\n";
+ StringExtensions.AppendInvariant(payload,$"TEL:{_mobilePhone}\r\n");
if (!string.IsNullOrEmpty(_workPhone))
- payload += $"TEL:{_workPhone}\r\n";
+ StringExtensions.AppendInvariant(payload,$"TEL:{_workPhone}\r\n");
if (!string.IsNullOrEmpty(_email))
- payload += $"EMAIL:{_email}\r\n";
+ StringExtensions.AppendInvariant(payload,$"EMAIL:{_email}\r\n");
if (!string.IsNullOrEmpty(_note))
- payload += $"NOTE:{_note}\r\n";
+ StringExtensions.AppendInvariant(payload,$"NOTE:{_note}\r\n");
if (_birthday != null)
- payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\r\n";
+ StringExtensions.AppendInvariant(payload,$"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\r\n");
// RFC 2426 Section 3.2.1: ADR format is PO Box; Extended Address; Street; Locality (City); Region; Postal Code; Country
- string addressString = string.Empty;
if (_addressOrder == AddressOrder.Default)
{
- addressString = $"ADR:,,{(!string.IsNullOrEmpty(_street) ? _street + " " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")},{(!string.IsNullOrEmpty(_city) ? _city : "")},{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")},{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")},{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n";
+ StringExtensions.AppendInvariant(payload,$"ADR:,,{(!string.IsNullOrEmpty(_street) ? $"{_street} " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")},{(!string.IsNullOrEmpty(_city) ? _city : "")},{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")},{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")},{(!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";
+ StringExtensions.AppendInvariant(payload,$"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";
+ StringExtensions.AppendInvariant(payload,$"URL:{_website}\r\n");
if (!string.IsNullOrEmpty(_nickname))
- payload += $"NICKNAME:{_nickname}\r\n";
- payload = payload.Trim(_trimChars);
+ StringExtensions.AppendInvariant(payload,$"NICKNAME:{_nickname}\r\n");
+ return payload.ToString().Trim(_trimChars);
}
- 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";
- }
-
- 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";
- }
+ var version = _outputType.ToString().Substring(5);
+ if (version.Length > 1)
+ version = version.Insert(1, ".");
+ else
+ version += ".0";
+ payload.Append("BEGIN:VCARD\r\n");
+ StringExtensions.AppendInvariant(payload,$"VERSION:{version}\r\n");
- // RFC 2426 Section 3.2.1: ADR format is PO Box; Extended Address; Street; Locality (City); Region; Postal Code; Country
- payload += "ADR;";
+ StringExtensions.AppendInvariant(payload,$"N:{(!string.IsNullOrEmpty(_lastname) ? _lastname : "")};{(!string.IsNullOrEmpty(_firstname) ? _firstname : "")};;;\r\n");
+ StringExtensions.AppendInvariant(payload,$"FN:{(!string.IsNullOrEmpty(_firstname) ? $"{_firstname} " : "")}{(!string.IsNullOrEmpty(_lastname) ? _lastname : "")}\r\n");
+ if (!string.IsNullOrEmpty(_org))
+ StringExtensions.AppendInvariant(payload,$"ORG:{_org}\r\n");
+ if (!string.IsNullOrEmpty(_orgTitle))
+ StringExtensions.AppendInvariant(payload,$"TITLE:{_orgTitle}\r\n");
+ if (!string.IsNullOrEmpty(_phone))
+ {
+ payload.Append("TEL;");
if (_outputType == ContactOutputType.VCard21)
- payload += GetAddressTypeString21() + ":";
+ StringExtensions.AppendInvariant(payload,$"HOME;VOICE:{_phone}");
else if (_outputType == ContactOutputType.VCard3)
- payload += "TYPE=" + GetAddressTypeString3() + ":";
+ StringExtensions.AppendInvariant(payload,$"TYPE=HOME,VOICE:{_phone}");
else
- payload += "TYPE=" + GetAddressTypeString4() + ":";
- string addressString = string.Empty;
- if (_addressOrder == AddressOrder.Default)
- {
- addressString = $";;{(!string.IsNullOrEmpty(_street) ? _street + " " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")};{(!string.IsNullOrEmpty(_city) ? _city : "")};{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")};{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")};{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n";
- }
+ StringExtensions.AppendInvariant(payload,$"TYPE=home,voice;VALUE=uri:tel:{_phone}");
+ payload.Append("\r\n");
+ }
+
+ if (!string.IsNullOrEmpty(_mobilePhone))
+ {
+ payload.Append("TEL;");
+ if (_outputType == ContactOutputType.VCard21)
+ StringExtensions.AppendInvariant(payload,$"HOME;CELL:{_mobilePhone}");
+ else if (_outputType == ContactOutputType.VCard3)
+ StringExtensions.AppendInvariant(payload,$"TYPE=HOME,CELL:{_mobilePhone}");
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;
+ StringExtensions.AppendInvariant(payload,$"TYPE=home,cell;VALUE=uri:tel:{_mobilePhone}");
+ payload.Append("\r\n");
+ }
- if (_birthday != null)
- payload += $"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\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";
+ if (!string.IsNullOrEmpty(_workPhone))
+ {
+ payload.Append("TEL;");
+ if (_outputType == ContactOutputType.VCard21)
+ StringExtensions.AppendInvariant(payload,$"WORK;VOICE:{_workPhone}");
+ else if (_outputType == ContactOutputType.VCard3)
+ StringExtensions.AppendInvariant(payload,$"TYPE=WORK,VOICE:{_workPhone}");
+ else
+ StringExtensions.AppendInvariant(payload,$"TYPE=work,voice;VALUE=uri:tel:{_workPhone}");
+ payload.Append("\r\n");
+ }
- payload += "END:VCARD";
+ // RFC 2426 Section 3.2.1: ADR format is PO Box; Extended Address; Street; Locality (City); Region; Postal Code; Country
+ payload.Append("ADR;");
+ if (_outputType == ContactOutputType.VCard21)
+ StringExtensions.AppendInvariant(payload,$"{GetAddressTypeString21()}:");
+ else if (_outputType == ContactOutputType.VCard3)
+ StringExtensions.AppendInvariant(payload,$"TYPE={GetAddressTypeString3()}:");
+ else
+ StringExtensions.AppendInvariant(payload,$"TYPE={GetAddressTypeString4()}:");
+ if (_addressOrder == AddressOrder.Default)
+ {
+ StringExtensions.AppendInvariant(payload,$";;{(!string.IsNullOrEmpty(_street) ? $"{_street} " : "")}{(!string.IsNullOrEmpty(_houseNumber) ? _houseNumber : "")};{(!string.IsNullOrEmpty(_city) ? _city : "")};{(!string.IsNullOrEmpty(_stateRegion) ? _stateRegion : "")};{(!string.IsNullOrEmpty(_zipCode) ? _zipCode : "")};{(!string.IsNullOrEmpty(_country) ? _country : "")}\r\n");
+ }
+ else
+ {
+ StringExtensions.AppendInvariant(payload,$";;{(!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");
}
- return payload;
+ if (_birthday != null)
+ StringExtensions.AppendInvariant(payload,$"BDAY:{((DateTime)_birthday).ToString("yyyyMMdd", CultureInfo.InvariantCulture)}\r\n");
+ if (!string.IsNullOrEmpty(_website))
+ StringExtensions.AppendInvariant(payload,$"URL:{_website}\r\n");
+ if (!string.IsNullOrEmpty(_email))
+ StringExtensions.AppendInvariant(payload,$"EMAIL:{_email}\r\n");
+ if (!string.IsNullOrEmpty(_note))
+ StringExtensions.AppendInvariant(payload,$"NOTE:{_note}\r\n");
+ if (_outputType != ContactOutputType.VCard21 && !string.IsNullOrEmpty(_nickname))
+ StringExtensions.AppendInvariant(payload,$"NICKNAME:{_nickname}\r\n");
+
+ payload.Append("END:VCARD");
+ return payload.ToString();
}
///
diff --git a/QRCoder/PayloadGenerator/Girocode.cs b/QRCoder/PayloadGenerator/Girocode.cs
index fedd794d..9d989560 100644
--- a/QRCoder/PayloadGenerator/Girocode.cs
+++ b/QRCoder/PayloadGenerator/Girocode.cs
@@ -74,24 +74,22 @@ public Girocode(string iban, string? bic, string name, decimal amount, string re
/// 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 += string.Format(CultureInfo.InvariantCulture, "EUR{0:0.00}", _amount) + _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("_", "-"));
+ var girocodePayload = new StringBuilder();
+ StringExtensions.AppendInvariant(girocodePayload,$"BCD{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{(_version == GirocodeVersion.Version1 ? "001" : "002")}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{(int)_encoding + 1}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"SCT{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{_bic}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{_name}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{_iban}{_br}");
+ girocodePayload.Append(string.Format(CultureInfo.InvariantCulture, "EUR{0:0.00}", _amount));
+ girocodePayload.Append(_br);
+ StringExtensions.AppendInvariant(girocodePayload,$"{_purposeOfCreditTransfer}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{(_typeOfRemittance == TypeOfRemittance.Structured ? _remittanceInformation : string.Empty)}{_br}");
+ StringExtensions.AppendInvariant(girocodePayload,$"{(_typeOfRemittance == TypeOfRemittance.Unstructured ? _remittanceInformation : string.Empty)}{_br}");
+ girocodePayload.Append(_messageToGirocodeUser);
+
+ return ConvertStringToEncoding(girocodePayload.ToString(), _encoding.ToString().Replace("_", "-"));
}
///
diff --git a/QRCoder/PayloadGenerator/Mail.cs b/QRCoder/PayloadGenerator/Mail.cs
index 76fbf7d8..6b211104 100644
--- a/QRCoder/PayloadGenerator/Mail.cs
+++ b/QRCoder/PayloadGenerator/Mail.cs
@@ -37,9 +37,9 @@ public override string ToString()
case MailEncoding.MAILTO:
var parts = new List();
if (!string.IsNullOrEmpty(_subject))
- parts.Add("subject=" + Uri.EscapeDataString(_subject));
+ parts.Add($"subject={Uri.EscapeDataString(_subject)}");
if (!string.IsNullOrEmpty(_message))
- parts.Add("body=" + Uri.EscapeDataString(_message));
+ parts.Add($"body={Uri.EscapeDataString(_message)}");
var queryString = parts.Count > 0 ? $"?{string.Join("&", parts.ToArray())}" : "";
return $"mailto:{_mailReceiver}{queryString}";
case MailEncoding.MATMSG:
diff --git a/QRCoder/PayloadGenerator/OneTimePassword.cs b/QRCoder/PayloadGenerator/OneTimePassword.cs
index e81eeb87..536e4ec6 100644
--- a/QRCoder/PayloadGenerator/OneTimePassword.cs
+++ b/QRCoder/PayloadGenerator/OneTimePassword.cs
@@ -141,7 +141,7 @@ private string HMACToString()
var sb = new StringBuilder("otpauth://hotp/");
ProcessCommonFields(sb);
var actualCounter = Counter ?? 1;
- sb.Append("&counter=" + actualCounter);
+ StringExtensions.AppendInvariant(sb,$"&counter={actualCounter}");
return sb.ToString();
}
@@ -162,7 +162,7 @@ private string TimeToString()
if (Period != 30)
{
- sb.Append("&period=" + Period);
+ StringExtensions.AppendInvariant(sb,$"&period={Period}");
}
return sb.ToString();
@@ -203,7 +203,7 @@ private void ProcessCommonFields(StringBuilder sb)
if (escapedLabel != null && escapedIssuer != null)
{
- label = escapedIssuer + ":" + escapedLabel;
+ label = $"{escapedIssuer}:{escapedLabel}";
}
else if (escapedIssuer != null)
{
@@ -215,21 +215,21 @@ private void ProcessCommonFields(StringBuilder sb)
sb.Append(label);
}
- sb.Append("?secret=" + strippedSecret);
+ StringExtensions.AppendInvariant(sb,$"?secret={strippedSecret}");
if (escapedIssuer != null)
{
- sb.Append("&issuer=" + escapedIssuer);
+ StringExtensions.AppendInvariant(sb,$"&issuer={escapedIssuer}");
}
if (AuthAlgorithm != OneTimePasswordAuthAlgorithm.SHA1)
{
- sb.Append("&algorithm=" + AuthAlgorithm.ToString());
+ StringExtensions.AppendInvariant(sb,$"&algorithm={AuthAlgorithm}");
}
if (Digits != 6)
{
- sb.Append("&digits=" + Digits);
+ StringExtensions.AppendInvariant(sb,$"&digits={Digits}");
}
}
}
diff --git a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs
index adc6d159..2239b3cf 100644
--- a/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs
+++ b/QRCoder/PayloadGenerator/RussiaPaymentOrder.cs
@@ -90,12 +90,13 @@ public byte[] ToBytes()
_separator = DetermineSeparator();
//Create the payload string
- string ret = $"ST0001" + ((int)_characterSet).ToString(CultureInfo.InvariantCulture) + //(separator != "|" ? separator : "") +
- $"{_separator}Name={_mFields.Name}" +
- $"{_separator}PersonalAcc={_mFields.PersonalAcc}" +
- $"{_separator}BankName={_mFields.BankName}" +
- $"{_separator}BIC={_mFields.BIC}" +
- $"{_separator}CorrespAcc={_mFields.CorrespAcc}";
+ var retBuilder = new StringBuilder($"ST0001{(int)_characterSet}");
+ StringExtensions.AppendInvariant(retBuilder,$"{_separator}Name={_mFields.Name}");
+ StringExtensions.AppendInvariant(retBuilder,$"{_separator}PersonalAcc={_mFields.PersonalAcc}");
+ StringExtensions.AppendInvariant(retBuilder,$"{_separator}BankName={_mFields.BankName}");
+ StringExtensions.AppendInvariant(retBuilder,$"{_separator}BIC={_mFields.BIC}");
+ StringExtensions.AppendInvariant(retBuilder,$"{_separator}CorrespAcc={_mFields.CorrespAcc}");
+ var ret = retBuilder.ToString();
//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;
@@ -576,7 +577,7 @@ public string? TaxPaytKind
}
#pragma warning disable CA1707 // Underscore in identifier
- ///
+ ///
/// (List of values of the technical code of the payment)
/// Перечень значений технического кода платежа
///
diff --git a/QRCoder/PayloadGenerator/SwissQrCode.cs b/QRCoder/PayloadGenerator/SwissQrCode.cs
index 57384154..2cd6aadc 100644
--- a/QRCoder/PayloadGenerator/SwissQrCode.cs
+++ b/QRCoder/PayloadGenerator/SwissQrCode.cs
@@ -8,7 +8,7 @@ public static partial class PayloadGenerator
public class SwissQrCode : Payload
{
//Keep in mind, that the ECC level has to be set to "M" when generating a SwissQrCode!
- //SwissQrCode specification:
+ //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
@@ -157,7 +157,7 @@ public class Reference
///
/// Type of the reference (QRR, SCOR or NON)
/// Reference text
- /// Type of the reference text (QR-reference or Creditor Reference)
+ /// Type of the reference text (QR-reference or Creditor Reference)
public Reference(ReferenceType referenceType, string? reference = null, ReferenceTextType? referenceTextType = null)
{
RefType = referenceType;
@@ -479,14 +479,15 @@ private static HashSet ValidTwoLetterCodes()
/// 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;
+ var contactData = new StringBuilder();
+ StringExtensions.AppendInvariant(contactData,$"{(AddressType.StructuredAddress == _adrType ? "S" : "K")}{_br}"); //AdrTp
+ StringExtensions.AppendInvariant(contactData,$"{_name.Replace("\n", "")}{_br}"); //Name
+ StringExtensions.AppendInvariant(contactData,$"{(!string.IsNullOrEmpty(_streetOrAddressline1) ? _streetOrAddressline1!.Replace("\n", "") : string.Empty)}{_br}"); //StrtNmOrAdrLine1
+ StringExtensions.AppendInvariant(contactData,$"{(!string.IsNullOrEmpty(_houseNumberOrAddressline2) ? _houseNumberOrAddressline2!.Replace("\n", "") : string.Empty)}{_br}"); //BldgNbOrAdrLine2
+ StringExtensions.AppendInvariant(contactData,$"{_zipCode.Replace("\n", "")}{_br}"); //PstCd
+ StringExtensions.AppendInvariant(contactData,$"{_city.Replace("\n", "")}{_br}"); //TwnNm
+ StringExtensions.AppendInvariant(contactData,$"{_country}{_br}"); //Ctry
+ return contactData.ToString();
}
///
@@ -543,58 +544,65 @@ public SwissQrCodeContactException(string message, Exception inner)
/// A string representing the Swiss QR code payload.
public override string ToString()
{
+ static void AppendEmptyAddressLines(StringBuilder builder, string lineBreak, int count)
+ {
+ for (int i = 0; i < count; i++)
+ builder.Append(lineBreak);
+ }
+
//Header "logical" element
- var SwissQrCodePayload = "SPC" + _br; //QRType
- SwissQrCodePayload += "0200" + _br; //Version
- SwissQrCodePayload += "1" + _br; //Coding
+ var swissQrCodePayload = new StringBuilder();
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"SPC{_br}"); //QRType
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"0200{_br}"); //Version
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"1{_br}"); //Coding
//CdtrInf "logical" element
- SwissQrCodePayload += _iban.ToString() + _br; //IBAN
-
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_iban}{_br}"); //IBAN
//Cdtr "logical" element
- SwissQrCodePayload += _creditor.ToString();
+ swissQrCodePayload.Append(_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());
+ AppendEmptyAddressLines(swissQrCodePayload, _br, 7);
//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
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{(_amount != null ? $"{_amount:0.00}".Replace(",", ".") : string.Empty)}{_br}"); //Amt
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_currency}{_br}"); //Ccy
//Removed in S-QR version 2.0
- //SwissQrCodePayload += (requestedDateOfPayment != null ? ((DateTime)requestedDateOfPayment).ToString("yyyy-MM-dd") : string.Empty) + br; //ReqdExctnDt
+ //StringExtensions.AppendInvariant(swissQrCodePayload,$"{(requestedDateOfPayment != null ? ((DateTime)requestedDateOfPayment).ToString("yyyy-MM-dd", CultureInfo.InvariantCulture) : string.Empty)}{_br}"); //ReqdExctnDt
//UltmtDbtr "logical" element
if (_debitor != null)
- SwissQrCodePayload += _debitor.ToString();
+ swissQrCodePayload.Append(_debitor.ToString());
else
- SwissQrCodePayload += string.Concat(Enumerable.Repeat(_br, 7).ToArray());
-
+ AppendEmptyAddressLines(swissQrCodePayload, _br, 7);
//RmtInf "logical" element
- SwissQrCodePayload += _reference.RefType.ToString() + _br; //Tp
- SwissQrCodePayload += (!string.IsNullOrEmpty(_reference.ReferenceText) ? _reference.ReferenceText : string.Empty) + _br; //Ref
-
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_reference.RefType}{_br}"); //Tp
+ StringExtensions.AppendInvariant(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
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{(!string.IsNullOrEmpty(_additionalInformation.UnstructureMessage) ? _additionalInformation.UnstructureMessage : string.Empty)}{_br}"); //Ustrd
+ StringExtensions.AppendInvariant(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 (!string.IsNullOrEmpty(_additionalInformation.BillInformation))
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_additionalInformation.BillInformation}{_br}"); //StrdBkgInf
//AltPmtInf "logical" element
if (!string.IsNullOrEmpty(_alternativeProcedure1))
- SwissQrCodePayload += _alternativeProcedure1!.Replace("\n", "") + _br; //AltPmt
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_alternativeProcedure1!.Replace("\n", "")}{_br}"); //AltPmt
if (!string.IsNullOrEmpty(_alternativeProcedure2))
- SwissQrCodePayload += _alternativeProcedure2!.Replace("\n", "") + _br; //AltPmt
+ StringExtensions.AppendInvariant(swissQrCodePayload,$"{_alternativeProcedure2!.Replace("\n", "")}{_br}"); //AltPmt
+
+ var result = swissQrCodePayload.ToString();
//S-QR specification 2.0, chapter 4.2.3
- if (SwissQrCodePayload.EndsWith(_br, StringComparison.Ordinal))
- SwissQrCodePayload = SwissQrCodePayload.Remove(SwissQrCodePayload.Length - _br.Length);
+ if (result.EndsWith(_br, StringComparison.Ordinal))
+ result = result.Remove(result.Length - _br.Length);
- return SwissQrCodePayload;
+ return result;
}
diff --git a/QRCoder/PayloadGenerator/Url.cs b/QRCoder/PayloadGenerator/Url.cs
index 08cd43a3..a94dee8f 100644
--- a/QRCoder/PayloadGenerator/Url.cs
+++ b/QRCoder/PayloadGenerator/Url.cs
@@ -23,6 +23,6 @@ public Url(string url)
///
/// 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;
+ => !_url.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? $"http://{_url}" : _url;
}
}
diff --git a/QRCoder/PayloadGenerator/WiFi.cs b/QRCoder/PayloadGenerator/WiFi.cs
index f9c45781..27631a37 100644
--- a/QRCoder/PayloadGenerator/WiFi.cs
+++ b/QRCoder/PayloadGenerator/WiFi.cs
@@ -21,9 +21,9 @@ public class WiFi : Payload
public WiFi(string ssid, string password, Authentication authenticationMode, bool isHiddenSSID = false, bool escapeHexStrings = true)
{
_ssid = EscapeInput(ssid);
- _ssid = escapeHexStrings && isHexStyle(_ssid) ? "\"" + _ssid + "\"" : _ssid;
+ _ssid = escapeHexStrings && isHexStyle(_ssid) ? $"\"{_ssid}\"" : _ssid;
_password = EscapeInput(password);
- _password = escapeHexStrings && isHexStyle(_password) ? "\"" + _password + "\"" : _password;
+ _password = escapeHexStrings && isHexStyle(_password) ? $"\"{_password}\"" : _password;
_authenticationMode = authenticationMode.ToString();
_isHiddenSsid = isHiddenSSID;
}
diff --git a/QRCoder/PdfByteQRCode.cs b/QRCoder/PdfByteQRCode.cs
index ae994813..d2edc437 100644
--- a/QRCoder/PdfByteQRCode.cs
+++ b/QRCoder/PdfByteQRCode.cs
@@ -78,97 +78,100 @@ public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string li
xrefs.Add(stream.Position);
// Object 1: Catalog - root of PDF document structure
- writer.Write(
- ToStr(xrefs.Count) + " 0 obj\r\n" + // Object number and generation number (0)
- "<<\r\n" + // Begin dictionary
- "/Type /Catalog\r\n" + // Declares this as the document catalog
- "/Pages 2 0 R\r\n" + // References the Pages object (object 2)
- ">>\r\n" + // End dictionary
- "endobj\r\n" // End object
- );
+ var catalogObject = new StringBuilder();
+ StringExtensions.AppendInvariant(catalogObject, $"{ToStr(xrefs.Count)} 0 obj\r\n"); // Object number and generation number (0)
+ catalogObject.Append("<<\r\n"); // Begin dictionary
+ catalogObject.Append("/Type /Catalog\r\n"); // Declares this as the document catalog
+ catalogObject.Append("/Pages 2 0 R\r\n"); // References the Pages object (object 2)
+ catalogObject.Append(">>\r\n"); // End dictionary
+ catalogObject.Append("endobj\r\n"); // End object
+ writer.Write(catalogObject.ToString());
writer.Flush();
xrefs.Add(stream.Position);
// Object 2: Pages - defines page tree structure
- writer.Write(
- ToStr(xrefs.Count) + " 0 obj\r\n" + // Object number and generation number (0)
- "<<\r\n" + // Begin dictionary
- "/Count 1\r\n" + // Number of pages in document
- "/Kids [ 3 0 R ]\r\n" + // Kids must contain indirect references to Page objects
- ">>\r\n" + // End dictionary
- "endobj\r\n" // End object
- );
+ var pagesObject = new StringBuilder();
+ StringExtensions.AppendInvariant(pagesObject, $"{ToStr(xrefs.Count)} 0 obj\r\n"); // Object number and generation number (0)
+ pagesObject.Append("<<\r\n"); // Begin dictionary
+ pagesObject.Append("/Count 1\r\n"); // Number of pages in document
+ pagesObject.Append("/Kids [ 3 0 R ]\r\n"); // Kids must contain indirect references to Page objects
+ pagesObject.Append(">>\r\n"); // End dictionary
+ pagesObject.Append("endobj\r\n"); // End object
+ writer.Write(pagesObject.ToString());
// Content stream - PDF drawing instructions
- var scale = ToStr(imgSize * 72 / (float)dpi / moduleCount); // Scale factor to convert module units to PDF points
- var pathCommands = CreatePathFromModules(); // Create path from dark modules
- var content = "q\r\n" + // 'q' = Save graphics state
- scale + " 0 0 -" + scale + " 0 " + pdfMediaSize + " cm\r\n" + // 'cm' = Transformation matrix: scale X, scale & flip Y, translate to top
- lightColorPdf + " rg\r\n" + // 'rg' = Set RGB fill color for background
- "0 0 " + ToStr(moduleCount) + " " + ToStr(moduleCount) + " re\r\n" + // 're' = Rectangle covering entire QR code
- "f\r\n" + // 'f' = Fill background
- darkColorPdf + " rg\r\n" + // 'rg' = Set RGB fill color for dark modules
- pathCommands + // Add all dark module rectangles to path
- "f*\r\n" + // 'f*' = Fill with even-odd rule
- "Q"; // 'Q' = Restore graphics state
+ var scale = ToStr(imgSize * 72 / (float)dpi / moduleCount); // Scale factor to convert module units to PDF points
+ var pathCommands = CreatePathFromModules(); // Create path from dark modules
+ var content = new StringBuilder();
+ content.Append("q\r\n"); // 'q' = Save graphics state
+ StringExtensions.AppendInvariant(content, $"{scale} 0 0 -{scale} 0 {pdfMediaSize} cm\r\n"); // 'cm' = Transformation matrix: scale X, scale & flip Y, translate to top
+ StringExtensions.AppendInvariant(content, $"{lightColorPdf} rg\r\n"); // 'rg' = Set RGB fill color for background
+ StringExtensions.AppendInvariant(content, $"0 0 {ToStr(moduleCount)} {ToStr(moduleCount)} re\r\n"); // 're' = Rectangle covering entire QR code
+ content.Append("f\r\n"); // 'f' = Fill background
+ StringExtensions.AppendInvariant(content, $"{darkColorPdf} rg\r\n"); // 'rg' = Set RGB fill color for dark modules
+ StringExtensions.AppendInvariant(content, $"{pathCommands}f*\r\n"); // Add all dark module rectangles to path; 'f*' = Fill with even-odd rule
+ content.Append('Q'); // 'Q' = Restore graphics state
+ var contentString = content.ToString();
writer.Flush();
xrefs.Add(stream.Position);
// Object 3: Page - indirect page object (Kids array must reference pages indirectly)
- writer.Write(
- ToStr(xrefs.Count) + " 0 obj\r\n" + // Object number and generation number (0)
- "<<\r\n" + // Begin dictionary
- "/Type /Page\r\n" + // Declares this as a page
- "/Parent 2 0 R\r\n" + // References parent Pages object
- "/MediaBox [0 0 " + pdfMediaSize + " " + pdfMediaSize + "]\r\n" + // Page dimensions [x1 y1 x2 y2]
- "/Resources << /ProcSet [ /PDF ] >>\r\n" + // Required resources: PDF operations only (no images)
- "/Contents 4 0 R\r\n" + // References content stream (object 4)
- ">>\r\n" + // End dictionary
- "endobj\r\n" // End object
- );
+ var pageObject = new StringBuilder();
+ StringExtensions.AppendInvariant(pageObject, $"{ToStr(xrefs.Count)} 0 obj\r\n"); // Object number and generation number (0)
+ pageObject.Append("<<\r\n"); // Begin dictionary
+ pageObject.Append("/Type /Page\r\n"); // Declares this as a page
+ pageObject.Append("/Parent 2 0 R\r\n"); // References parent Pages object
+ StringExtensions.AppendInvariant(pageObject, $"/MediaBox [0 0 {pdfMediaSize} {pdfMediaSize}]\r\n"); // Page dimensions [x1 y1 x2 y2]
+ pageObject.Append("/Resources << /ProcSet [ /PDF ] >>\r\n"); // Required resources: PDF operations only (no images)
+ pageObject.Append("/Contents 4 0 R\r\n"); // References content stream (object 4)
+ pageObject.Append(">>\r\n"); // End dictionary
+ pageObject.Append("endobj\r\n"); // End object
+ writer.Write(pageObject.ToString());
writer.Flush();
xrefs.Add(stream.Position);
// Object 4: Content stream - contains the drawing instructions
- writer.Write(
- ToStr(xrefs.Count) + " 0 obj\r\n" + // Object number and generation number (0)
- "<< /Length " + ToStr(System.Text.Encoding.ASCII.GetByteCount(content)) + " >>\r\n" + // Dictionary with stream length in bytes
- "stream\r\n" + // Begin stream data
- content + "endstream\r\n" + // Stream content followed by end stream marker
- "endobj\r\n" // End object
- );
+ var contentStreamObject = new StringBuilder();
+ StringExtensions.AppendInvariant(contentStreamObject, $"{ToStr(xrefs.Count)} 0 obj\r\n"); // Object number and generation number (0)
+ StringExtensions.AppendInvariant(contentStreamObject, $"<< /Length {ToStr(System.Text.Encoding.ASCII.GetByteCount(contentString))} >>\r\n"); // Dictionary with stream length in bytes
+ StringExtensions.AppendInvariant(contentStreamObject, $"stream\r\n{contentString}endstream\r\n"); // Begin stream data; stream content followed by end stream marker
+ contentStreamObject.Append("endobj\r\n"); // End object
+ writer.Write(contentStreamObject.ToString());
writer.Flush();
var startxref = checked((int)stream.Position);
// Cross-reference table - maps object numbers to byte offsets
- writer.Write(
- "xref\r\n" + // Cross-reference table keyword
- "0 " + ToStr(xrefs.Count + 1) + "\r\n" + // First object number (0) and count of entries
- "0000000000 65535 f\r\n" // Entry 0: always free, generation 65535, 'f' = free
- );
+ var xrefTable = new StringBuilder();
+ xrefTable.Append("xref\r\n"); // Cross-reference table keyword
+ StringExtensions.AppendInvariant(xrefTable, $"0 {ToStr(xrefs.Count + 1)}\r\n"); // First object number (0) and count of entries
+ xrefTable.Append("0000000000 65535 f\r\n"); // Entry 0: always free, generation 65535, 'f' = free
+ writer.Write(xrefTable.ToString());
// Write byte offset for each object
+ var xrefEntry = new StringBuilder();
foreach (var refValue in xrefs)
{
// Write each entry as a 10-digit zero-padded byte offset, 5-digit zero-padded generation number (0), and 'n' = in use
- writer.Write(checked((int)refValue).ToString("0000000000", CultureInfo.InvariantCulture) + " 00000 n\r\n");
+ xrefEntry.Length = 0;
+ StringExtensions.AppendInvariant(xrefEntry, $"{checked((int)refValue).ToString("0000000000", CultureInfo.InvariantCulture)} 00000 n\r\n");
+ writer.Write(xrefEntry.ToString());
}
// Trailer - provides location of catalog and xref table
- writer.Write(
- "trailer\r\n" + // Trailer keyword
- "<<\r\n" + // Begin trailer dictionary
- "/Size " + ToStr(xrefs.Count + 1) + "\r\n" + // Total number of entries in xref table
- "/Root 1 0 R\r\n" + // Reference to catalog object
- ">>\r\n" + // End trailer dictionary
- "startxref\r\n" + // Start of xref keyword
- ToStr(startxref) + "\r\n" + // Byte offset of xref table
- "%%EOF" // End of file marker
- );
+ var trailer = new StringBuilder();
+ trailer.Append("trailer\r\n"); // Trailer keyword
+ trailer.Append("<<\r\n"); // Begin trailer dictionary
+ StringExtensions.AppendInvariant(trailer, $"/Size {ToStr(xrefs.Count + 1)}\r\n"); // Total number of entries in xref table
+ trailer.Append("/Root 1 0 R\r\n"); // Reference to catalog object
+ trailer.Append(">>\r\n"); // End trailer dictionary
+ trailer.Append("startxref\r\n"); // Start of xref keyword
+ StringExtensions.AppendInvariant(trailer, $"{ToStr(startxref)}\r\n"); // Byte offset of xref table
+ trailer.Append("%%EOF"); // End of file marker
+ writer.Write(trailer.ToString());
writer.Flush();
}
@@ -214,12 +217,7 @@ private string CreatePathFromModules()
// Create a single rectangle for the entire run of dark modules
// Format: x y width height re
- pathCommands.AppendInvariant(startX);
- pathCommands.Append(' ');
- pathCommands.AppendInvariant(y);
- pathCommands.Append(' ');
- pathCommands.AppendInvariant(x - startX);
- pathCommands.Append(" 1 re\r\n");
+ StringExtensions.AppendInvariant(pathCommands, $"{startX} {y} {x - startX} 1 re\r\n");
}
}
@@ -241,7 +239,7 @@ private static string ColorToPdfRgb(byte[] color)
var g = ToStr(color[1] * inv255);
var b = ToStr(color[2] * inv255);
- return r + " " + g + " " + b;
+ return $"{r} {g} {b}";
}
///
diff --git a/QRCoder/QRCodeGenerator/Polynom.cs b/QRCoder/QRCodeGenerator/Polynom.cs
index 90237945..a1ed208d 100644
--- a/QRCoder/QRCodeGenerator/Polynom.cs
+++ b/QRCoder/QRCodeGenerator/Polynom.cs
@@ -154,7 +154,7 @@ public override string ToString()
for (int i = 0; i < Count; i++)
{
var polyItem = _polyItems[i];
- sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + ");
+ StringExtensions.AppendInvariant(sb,$"a^{polyItem.Coefficient}*x^{polyItem.Exponent} + ");
}
// Remove the trailing " + " if the string builder has added terms