diff --git a/.github/workflows/auto-pr-from-main.yml b/.github/workflows/auto-pr-from-main.yml index b05b9d1..6f3411f 100644 --- a/.github/workflows/auto-pr-from-main.yml +++ b/.github/workflows/auto-pr-from-main.yml @@ -45,12 +45,42 @@ jobs: git checkout -b "$BRANCH_NAME" # List outdated packages - dotnet list QRCoder.Core.sln package --outdated + dotnet list QRCoder.Core.sln package --outdated --format json > outdated-packages.json - # Update packages (this is a simplified approach) + # Update packages using the latest available versions echo "📦 Updating NuGet packages..." - dotnet add src/Metar.Decoder package --version latest || echo "No updates needed for Metar.Decoder" - dotnet add src/Taf.Decoder package --version latest || echo "No updates needed for Taf.Decoder" + python3 - <<'PY' + import json + import pathlib + import subprocess + + data = json.loads(pathlib.Path("outdated-packages.json").read_text()) + updates = {} + + for project in data.get("projects", []): + project_path = pathlib.Path(project["path"]).resolve() + for framework in project.get("frameworks", []): + for package in framework.get("topLevelPackages", []): + latest = package.get("latestVersion") + requested = package.get("requestedVersion") + if latest and latest != requested: + updates.setdefault(project_path, {})[package["id"]] = latest + + for project_path, packages in updates.items(): + for package_name, latest_version in sorted(packages.items()): + subprocess.run( + [ + "dotnet", + "add", + str(project_path), + "package", + package_name, + "--version", + latest_version, + ], + check=True, + ) + PY # Check if there are any changes if git diff --quiet; then diff --git a/.github/workflows/ci-build-test.yml b/.github/workflows/ci-build-test.yml index 839ace4..944a670 100644 --- a/.github/workflows/ci-build-test.yml +++ b/.github/workflows/ci-build-test.yml @@ -312,8 +312,8 @@ jobs: - ✅ Performance Tests --- - *Este PR foi criado automaticamente pelo pipeline CI/CD* - branch: main + *Este PR foi criado automaticamente pelo pipeline CI/CD* + branch: auto-pr/${{ github.ref_name }} delete-branch: true draft: false labels: | diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 9785b43..1aa1a63 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -100,7 +100,7 @@ jobs: echo "✅ SONNAR_TOKEN is configured" dotnet sonarscanner begin \ /o:"afonsoft" \ - /k:"afonsoft_metar-decoder" \ + /k:"afonsoft_QRCoder.Core" \ /d:sonar.host.url="https://sonarcloud.io" \ /d:sonar.login="$SONAR_TOKEN" \ /d:sonar.scm.provider=git \ diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 021f938..6107f2c 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -125,7 +125,7 @@ jobs: echo "✅ SONNAR_TOKEN is configured" dotnet sonarscanner begin \ /o:"afonsoft" \ - /k:"afonsoft_metar-decoder" \ + /k:"afonsoft_QRCoder.Core" \ /d:sonar.host.url="https://sonarcloud.io" \ /d:sonar.login="${{ secrets.SONNAR_TOKEN }}" \ /d:sonar.scm.provider=git \ diff --git a/QRCoder.Core/Generators/PayloadGenerator.cs b/QRCoder.Core/Generators/PayloadGenerator.cs index b63bcb8..5d6a2a2 100644 --- a/QRCoder.Core/Generators/PayloadGenerator.cs +++ b/QRCoder.Core/Generators/PayloadGenerator.cs @@ -627,136 +627,103 @@ public ContactData(ContactOutputType outputType, string firstname, string lastna /// The string result. public override string ToString() { - 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) - { - 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('\r', '\n'); - } - else - { - var version = outputType.ToString().Substring(5); - if (version.Length > 1) - version = version.Insert(1, "."); - else - version += ".0"; + return BuildMeCard(); - payload += "BEGIN:VCARD\r\n"; - payload += $"VERSION:{version}\r\n"; + return BuildVCard(); + } - 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"; - } + private string BuildMeCard() + { + var payload = new StringBuilder("MECARD+\r\n"); + AppendName(payload, "N:"); + AppendLine(payload, "ORG:", org); + AppendLine(payload, "TITLE:", orgTitle); + AppendLine(payload, "TEL:", phone); + AppendLine(payload, "TEL:", mobilePhone); + AppendLine(payload, "TEL:", workPhone); + AppendLine(payload, "EMAIL:", email); + AppendLine(payload, "NOTE:", note); + if (birthday != null) + AppendLine(payload, "BDAY:", ((DateTime)birthday).ToString("yyyyMMdd")); + payload.Append("ADR:,,").Append(BuildAddress(',')).Append("\r\n"); + AppendLine(payload, "URL:", website); + AppendLine(payload, "NICKNAME:", nickname); + return payload.ToString().TrimEnd('\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"; - } + private string BuildVCard() + { + var payload = new StringBuilder(); + var version = outputType.ToString().Substring(5); + if (version.Length > 1) + version = version.Insert(1, "."); + else + version += ".0"; + + payload.Append("BEGIN:VCARD\r\n"); + payload.Append("VERSION:").Append(version).Append("\r\n"); + payload.Append("N:").Append(!string.IsNullOrEmpty(lastname) ? lastname : "").Append(";").Append(!string.IsNullOrEmpty(firstname) ? firstname : "").Append(";;;\r\n"); + payload.Append("FN:").Append(!string.IsNullOrEmpty(firstname) ? firstname + " " : "").Append(!string.IsNullOrEmpty(lastname) ? lastname : "").Append("\r\n"); + AppendLine(payload, "ORG:", org); + AppendLine(payload, "TITLE:", orgTitle); + AppendTelephone(payload, phone, "HOME;VOICE:", "TYPE=HOME,VOICE:", "TYPE=home,voice;VALUE=uri:tel:"); + AppendTelephone(payload, mobilePhone, "HOME;CELL:", "TYPE=HOME,CELL:", "TYPE=home,cell;VALUE=uri:tel:"); + AppendTelephone(payload, workPhone, "WORK;VOICE:", "TYPE=WORK,VOICE:", "TYPE=work,voice;VALUE=uri:tel:"); + payload.Append("ADR;"); + if (outputType == ContactOutputType.VCard21) + payload.Append("HOME;PREF:"); + else if (outputType == ContactOutputType.VCard3) + payload.Append("TYPE=HOME,PREF:"); + else + payload.Append("TYPE=home,pref:"); + payload.Append(';').Append(';').Append(BuildAddress(';')).Append("\r\n"); + if (birthday != null) + AppendLine(payload, "BDAY:", ((DateTime)birthday).ToString("yyyyMMdd")); + AppendLine(payload, "URL:", website); + AppendLine(payload, "EMAIL:", email); + AppendLine(payload, "NOTE:", note); + if (outputType != ContactOutputType.VCard21) + AppendLine(payload, "NICKNAME:", nickname); + payload.Append("END:VCARD"); + return payload.ToString(); + } - 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"; - } + private void AppendName(StringBuilder payload, string prefix) + { + if (!string.IsNullOrEmpty(firstname) && !string.IsNullOrEmpty(lastname)) + payload.Append(prefix).Append(lastname).Append(", ").Append(firstname).Append("\r\n"); + else if (!string.IsNullOrEmpty(firstname) || !string.IsNullOrEmpty(lastname)) + payload.Append(prefix).Append(firstname).Append(lastname).Append("\r\n"); + } - 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"; - - payload += "END:VCARD"; - } + private static void AppendLine(StringBuilder payload, string prefix, string value) + { + if (!string.IsNullOrEmpty(value)) + payload.Append(prefix).Append(value).Append("\r\n"); + } + + private void AppendTelephone(StringBuilder payload, string value, string v21Prefix, string v3Prefix, string v4Prefix) + { + if (string.IsNullOrEmpty(value)) + return; + + payload.Append("TEL;"); + if (outputType == ContactOutputType.VCard21) + payload.Append(v21Prefix).Append(value); + else if (outputType == ContactOutputType.VCard3) + payload.Append(v3Prefix).Append(value); + else + payload.Append(v4Prefix).Append(value); + payload.Append("\r\n"); + } - return payload; + private string BuildAddress(char separator) + { + if (addressOrder == AddressOrder.Default) + return $"{(!string.IsNullOrEmpty(street) ? street + " " : "")}{(!string.IsNullOrEmpty(houseNumber) ? houseNumber : "")}{separator}{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")}{separator}{(!string.IsNullOrEmpty(city) ? city : "")}{separator}{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")}{separator}{(!string.IsNullOrEmpty(country) ? country : "")}"; + + return $"{(!string.IsNullOrEmpty(houseNumber) ? houseNumber + " " : "")}{(!string.IsNullOrEmpty(street) ? street : "")}{separator}{(!string.IsNullOrEmpty(city) ? city : "")}{separator}{(!string.IsNullOrEmpty(stateRegion) ? stateRegion : "")}{separator}{(!string.IsNullOrEmpty(zipCode) ? zipCode : "")}{separator}{(!string.IsNullOrEmpty(country) ? country : "")}"; } /// @@ -2040,85 +2007,92 @@ public BezahlCode(AuthorityType authority, string name, string account, string b /// The string result. public override string ToString() { - var bezahlCodePayload = $"bank://{authority}?"; - - bezahlCodePayload += $"name={Uri.EscapeDataString(name)}&"; + var bezahlCodePayload = new StringBuilder($"bank://{authority}?"); + AppendParameter(bezahlCodePayload, "name", Uri.EscapeDataString(name)); if (authority != AuthorityType.contact && authority != AuthorityType.contact_v2) { - //Handle what is same for all payments + AppendPaymentParameters(bezahlCodePayload); + } + else + { + AppendContactParameters(bezahlCodePayload); + } + + return bezahlCodePayload.ToString().Trim('&'); + } + + private void AppendPaymentParameters(StringBuilder payload) + { #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 + { + AppendParameter(payload, "account", account); + AppendParameter(payload, "bnc", bnc); + if (postingKey > 0) + AppendParameter(payload, "postingkey", postingKey.ToString()); + } + else + { + AppendParameter(payload, "iban", iban); + AppendParameter(payload, "bic", bic); + AppendParameter(payload, "separeference", Uri.EscapeDataString(sepaReference)); + if (authority == AuthorityType.singledirectdebitsepa) { - bezahlCodePayload += $"account={account}&"; - bezahlCodePayload += $"bnc={bnc}&"; - if (postingKey > 0) - bezahlCodePayload += $"postingkey={postingKey}&"; + AppendParameter(payload, "creditorid", Uri.EscapeDataString(creditorId)); + AppendParameter(payload, "mandateid", Uri.EscapeDataString(mandateId)); + if (dateOfSignature != DateTime.MinValue) + AppendParameter(payload, "dateofsignature", dateOfSignature.ToString("ddMMyyyy")); } - 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(DateFormat)}&"; - } - } - bezahlCodePayload += $"amount={amount:0.00}&".Replace(".", ","); + AppendParameter(payload, "amount", amount.ToString("0.00").Replace(".", ",")); + AppendParameter(payload, "reason", Uri.EscapeDataString(reason)); + AppendParameter(payload, "currency", currency.ToString()); + AppendParameter(payload, "executiondate", executionDate.ToString("ddMMyyyy")); - if (!string.IsNullOrEmpty(reason)) - bezahlCodePayload += $"reason={Uri.EscapeDataString(reason)}&"; - bezahlCodePayload += $"currency={currency}&"; - bezahlCodePayload += $"executiondate={executionDate.ToString(DateFormat)}&"; #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(DateFormat)}&"; - if (periodicLastExecutionDate != DateTime.MinValue) - bezahlCodePayload += $"periodiclastexecutiondate={periodicLastExecutionDate.ToString(DateFormat)}&"; - } + if (authority == AuthorityType.periodicsinglepayment || authority == AuthorityType.periodicsinglepaymentsepa) + { + AppendParameter(payload, "periodictimeunit", periodicTimeunit); + AppendParameter(payload, "periodictimeunitrotation", periodicTimeunitRotation.ToString()); + if (periodicFirstExecutionDate != DateTime.MinValue) + AppendParameter(payload, "periodicfirstexecutiondate", periodicFirstExecutionDate.ToString("ddMMyyyy")); + if (periodicLastExecutionDate != DateTime.MinValue) + AppendParameter(payload, "periodiclastexecutiondate", periodicLastExecutionDate.ToString("ddMMyyyy")); + } #pragma warning restore CS0612 + } + + private void AppendContactParameters(StringBuilder payload) + { + if (authority == AuthorityType.contact) + { + AppendParameter(payload, "account", account); + AppendParameter(payload, "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}&"; + AppendParameter(payload, "account", account); + AppendParameter(payload, "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}&"; - } + AppendParameter(payload, "iban", iban); + AppendParameter(payload, "bic", bic); } - - if (!string.IsNullOrEmpty(reason)) - bezahlCodePayload += $"reason={Uri.EscapeDataString(reason)}&"; } - return bezahlCodePayload.Trim('&'); + AppendParameter(payload, "reason", Uri.EscapeDataString(reason)); + } + + private static void AppendParameter(StringBuilder payload, string key, string value) + { + if (!string.IsNullOrEmpty(value)) + payload.Append(key).Append('=').Append(value).Append('&'); } /// diff --git a/QRCoder.Core/Generators/QRCodeGenerator.cs b/QRCoder.Core/Generators/QRCodeGenerator.cs index 3eda726..8137346 100644 --- a/QRCoder.Core/Generators/QRCodeGenerator.cs +++ b/QRCoder.Core/Generators/QRCodeGenerator.cs @@ -25,7 +25,6 @@ public class QRCodeGenerator : IDisposable private static readonly List capacityTable = CreateCapacityTable(); private static readonly List galoisField = CreateAntilogTable(); private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); - private bool disposed; /// /// Specifies the ECI (Extended Channel Interpretation) mode for character encoding. @@ -193,105 +192,94 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) string modeIndicator = DecToBin((int)EncodingMode.Byte, 4); string countIndicator = DecToBin(binaryData.Length, GetCountIndicatorLength(version, EncodingMode.Byte)); - string bitString = modeIndicator + countIndicator; + var bitString = new StringBuilder(modeIndicator + countIndicator); foreach (byte b in binaryData) { - bitString += DecToBin(b, 8); + bitString.Append(DecToBin(b, 8)); } - return GenerateQrCode(bitString, eccLevel, version); + return GenerateQrCode(bitString.ToString(), eccLevel, version); } private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) { - //Fill up data code word var eccInfo = capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); - var dataLength = eccInfo.TotalDataCodewords * 8; - var lengthDiff = dataLength - bitString.Length; + var paddedBitString = PadToDataLength(bitString, eccInfo.TotalDataCodewords * 8); + var codeWordWithECC = CreateCodewordBlocks(paddedBitString, eccInfo); + var interleavedData = InterleaveCodewords(codeWordWithECC, eccInfo, version); + return PlaceModules(version, eccLevel, interleavedData); + } + + private static string PadToDataLength(string bitString, int dataLength) + { + var padded = new StringBuilder(bitString); + var lengthDiff = dataLength - padded.Length; if (lengthDiff > 0) - bitString += new string('0', Math.Min(lengthDiff, 4)); - if ((bitString.Length % 8) != 0) - bitString += new string('0', 8 - (bitString.Length % 8)); - while (bitString.Length < dataLength) - bitString += "1110110000010001"; - if (bitString.Length > dataLength) - bitString = bitString.Substring(0, dataLength); - - //Calculate error correction words + padded.Append(new string('0', Math.Min(lengthDiff, 4))); + if ((padded.Length % 8) != 0) + padded.Append(new string('0', 8 - (padded.Length % 8))); + while (padded.Length < dataLength) + padded.Append("1110110000010001"); + if (padded.Length > dataLength) + padded.Length = dataLength; + return padded.ToString(); + } + + private static List CreateCodewordBlocks(string bitString, EccInfo eccInfo) + { var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - for (var i = 0; i < eccInfo.BlocksInGroup1; i++) - { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup1 * 8, eccInfo.CodewordsInGroup1 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add( - new CodewordBlock(1, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); - } - bitString = bitString.Substring(eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8); - for (var i = 0; i < eccInfo.BlocksInGroup2; i++) + var offset = 0; + AddCodewordBlocks(codeWordWithECC, bitString, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, 1, ref offset, eccInfo); + AddCodewordBlocks(codeWordWithECC, bitString, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, 2, ref offset, eccInfo); + return codeWordWithECC; + } + + private static void AddCodewordBlocks(List codeWordWithECC, string bitString, int blockCount, int codewordsInBlock, int group, ref int offset, EccInfo eccInfo) + { + for (var i = 0; i < blockCount; i++) { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup2 * 8, eccInfo.CodewordsInGroup2 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add(new CodewordBlock(2, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); + var blockBitString = bitString.Substring(offset, codewordsInBlock * 8); + offset += codewordsInBlock * 8; + var bitBlockList = BinaryStringToBitBlockList(blockBitString); + var eccWordList = CalculateECCWords(blockBitString, eccInfo); + codeWordWithECC.Add(new CodewordBlock(group, i + 1, blockBitString, bitBlockList, eccWordList, BinaryStringListToDecList(bitBlockList), BinaryStringListToDecList(eccWordList))); } + } - //Interleave code words + private static string InterleaveCodewords(List codeWordWithECC, EccInfo eccInfo, int version) + { var interleavedWordsSb = new StringBuilder(); for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.CodeWords.Count > i) - interleavedWordsSb.Append(codeBlock.CodeWords[i]); + foreach (var codeWord in codeWordWithECC.Select(codeBlock => codeBlock.CodeWords).Where(codeWords => codeWords.Count > i).Select(codeWords => codeWords[i])) + interleavedWordsSb.Append(codeWord); } for (var i = 0; i < eccInfo.EccPerBlock; i++) { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Count > i) - interleavedWordsSb.Append(codeBlock.ECCWords[i]); + foreach (var eccWord in codeWordWithECC.Select(codeBlock => codeBlock.ECCWords).Where(eccWords => eccWords.Count > i).Select(eccWords => eccWords[i])) + interleavedWordsSb.Append(eccWord); } + interleavedWordsSb.Append(new string('0', remainderBits[version - 1])); - var interleavedData = interleavedWordsSb.ToString(); + return interleavedWordsSb.ToString(); + } - //Place interleaved data on module matrix + private static QRCodeData PlaceModules(int version, ECCLevel eccLevel, string interleavedData) + { var qr = new QRCodeData(version); var blockedModules = new List(); ModulePlacer.PlaceFinderPatterns(ref qr, ref blockedModules); ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, ref blockedModules); - ModulePlacer.PlaceAlignmentPatterns(ref qr, alignmentPatternTable.Where(x => x.Version == version).Select(x => x.PatternPositions).First(), ref blockedModules); + ModulePlacer.PlaceAlignmentPatterns(ref qr, alignmentPatternTable.First(x => x.Version == version).PatternPositions, ref blockedModules); ModulePlacer.PlaceTimingPatterns(ref qr, ref blockedModules); ModulePlacer.PlaceDarkModule(ref qr, version, ref blockedModules); ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, ref blockedModules); ModulePlacer.PlaceDataWords(ref qr, interleavedData, ref blockedModules); var maskVersion = ModulePlacer.MaskCode(ref qr, version, ref blockedModules, eccLevel); - var formatStr = GetFormatString(eccLevel, maskVersion); - - ModulePlacer.PlaceFormat(ref qr, formatStr); + ModulePlacer.PlaceFormat(ref qr, GetFormatString(eccLevel, maskVersion)); if (version >= 7) - { - var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(ref qr, versionString); - } - + ModulePlacer.PlaceVersion(ref qr, GetVersionString(version)); ModulePlacer.AddQuietZone(ref qr); return qr; } @@ -301,22 +289,7 @@ private static string GetFormatString(ECCLevel level, int maskVersion) var generator = "10100110111"; var fStrMask = "101010000010010"; - var fStr = string.Empty; - switch (level) - { - case ECCLevel.L: - fStr = "01"; - break; - case ECCLevel.M: - fStr = "00"; - break; - case ECCLevel.Q: - fStr = "11"; - break; - default: - fStr = "10"; - break; - } + var fStr = (level == ECCLevel.L) ? "01" : (level == ECCLevel.M) ? "00" : (level == ECCLevel.Q) ? "11" : "10"; fStr += DecToBin(maskVersion, 3); var fStrEcc = fStr.PadRight(15, '0').TrimStart('0'); while (fStrEcc.Length > 10) @@ -377,16 +350,13 @@ public static void AddQuietZone(ref QRCodeData qrCode) } } - private static string ReverseString(string inp) - { - string newStr = string.Empty; - if (inp.Length > 0) - { - for (int i = inp.Length - 1; i >= 0; i--) - newStr += inp[i]; - } - return newStr; - } + private static string ReverseString(string inp) + { + var sb = new StringBuilder(inp.Length); + for (var i = inp.Length - 1; i >= 0; i--) + sb.Append(inp[i]); + return sb.ToString(); + } public static void PlaceVersion(ref QRCodeData qrCode, string versionStr) { @@ -522,32 +492,31 @@ public static void PlaceDataWords(ref QRCodeData qrCode, string data, ref List= 0) + for (var x = size - 1; x >= 0; x = x - 2) { - var currentX = x == 6 ? 5 : x; + if (x == 6) + x = 5; for (var yMod = 1; yMod <= size; yMod++) { int y; if (up) { y = size - yMod; - if (datawords.Count > 0 && !IsBlocked(new SKRectI(currentX, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][currentX] = datawords.Dequeue(); - if (datawords.Count > 0 && currentX > 0 && !IsBlocked(new SKRectI(currentX - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][currentX - 1] = datawords.Dequeue(); + if (datawords.Count > 0 && !IsBlocked(new SKRectI(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); + if (datawords.Count > 0 && x > 0 && !IsBlocked(new SKRectI(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); } else { y = yMod - 1; - if (datawords.Count > 0 && !IsBlocked(new SKRectI(currentX, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][currentX] = datawords.Dequeue(); - if (datawords.Count > 0 && currentX > 0 && !IsBlocked(new SKRectI(currentX - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][currentX - 1] = datawords.Dequeue(); + if (datawords.Count > 0 && !IsBlocked(new SKRectI(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); + if (datawords.Count > 0 && x > 0 && !IsBlocked(new SKRectI(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); } } up = !up; - x = currentX - 2; } } @@ -610,22 +579,13 @@ public static void PlaceFinderPatterns(ref QRCodeData qrCode, ref List } } - public static void PlaceAlignmentPatterns(ref QRCodeData qrCode, List alignmentPatternLocations, ref List blockedModules) + public static void PlaceAlignmentPatterns(ref QRCodeData qrCode, List alignmentPatternLocations, ref List blockedModules) + { + foreach (var loc in alignmentPatternLocations) { - foreach (var loc in alignmentPatternLocations) - { - var alignmentPatternRect = new SKRectI(loc.X, loc.Y, 5, 5); - var blocked = false; - foreach (var blockedRect in blockedModules) - { - if (Intersects(alignmentPatternRect, blockedRect)) - { - blocked = true; - break; - } - } - if (blocked) - continue; + var alignmentPatternRect = new SKRectI(loc.X, loc.Y, 5, 5); + if (blockedModules.Any(blockedRect => Intersects(alignmentPatternRect, blockedRect))) + continue; for (var x = 0; x < 5; x++) { @@ -663,15 +623,10 @@ private static bool Intersects(SKRectI r1, SKRectI r2) return r2.X < r1.X + r1.Width && r1.X < r2.X + r2.Width && r2.Y < r1.Y + r1.Height && r1.Y < r2.Y + r2.Height; } - private static bool IsBlocked(SKRectI r1, List blockedModules) - { - foreach (var blockedMod in blockedModules) - { - if (Intersects(blockedMod, r1)) - return true; - } - return false; - } + private static bool IsBlocked(SKRectI r1, List blockedModules) + { + return blockedModules.Any(blockedMod => Intersects(blockedMod, r1)); + } private static class MaskPattern { @@ -717,44 +672,43 @@ public static bool Pattern8(int x, int y) public static int Score(ref QRCodeData qrCode) { - int score1 = 0, - score2 = 0, - score3 = 0, - score4 = 0; var size = qrCode.ModuleMatrix.Count; + return ScoreLines(qrCode, size) + ScoreBlocks(qrCode, size) + ScoreFinderPatterns(qrCode, size) + ScoreBalance(qrCode, size); + } - //Penalty 1 + private static int ScoreLines(QRCodeData qrCode, int size) + { + var score = 0; for (var y = 0; y < size; y++) { - 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++) - { - 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]; - - 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]; - } + score += ScoreLine(qrCode.ModuleMatrix[y].Cast().ToList()); + score += ScoreLine(qrCode.ModuleMatrix.Select(row => row[y]).ToList()); } + return score; + } + + private static int ScoreLine(IReadOnlyList line) + { + var score = 0; + var runLength = 1; + for (var i = 1; i < line.Count; i++) + { + if (line[i] == line[i - 1]) + runLength++; + else + runLength = 1; + + if (runLength == 5) + score += 3; + else if (runLength > 5) + score++; + } + return score; + } - //Penalty 2 + private static int ScoreBlocks(QRCodeData qrCode, int size) + { + var score = 0; for (var y = 0; y < size - 1; y++) { for (var x = 0; x < size - 1; x++) @@ -762,284 +716,114 @@ public static int Score(ref QRCodeData qrCode) 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; + score += 3; } } + return score; + } - //Penalty 3 + private static int ScoreFinderPatterns(QRCodeData qrCode, int size) + { + var score = 0; for (var y = 0; y < size; y++) { for (var x = 0; x < size - 10; x++) { - 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; - } - - 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; - } + if (MatchesFinderPattern(qrCode, x, y)) + score += 40; } } - //Penalty 4 - double blackModules = 0; - foreach (var row in qrCode.ModuleMatrix) - foreach (bool bit in row) - if (bit) - blackModules++; - - var percent = (blackModules / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count)) * 100; - var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 50) / 5; - var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5; - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; + for (var x = 0; x < size - 10; x++) + { + for (var y = 0; y < size; y++) + { + if (MatchesFinderPattern(qrCode, x, y, vertical: true)) + score += 40; + } + } - return score1 + score2 + score3 + score4; + return score; } - } - } - - private static List CalculateECCWords(string bitString, EccInfo eccInfo) - { - var eccWords = eccInfo.EccPerBlock; - var messagePolynom = CalculateMessagePolynom(bitString); - var generatorPolynom = CalculateGeneratorPolynom(eccWords); - - for (var index = 0; index < messagePolynom.PolyItems.Count; index++) - messagePolynom.PolyItems[index] = new PolynomItem(messagePolynom.PolyItems[index].Coefficient, - messagePolynom.PolyItems[index].Exponent + eccWords); - for (var index = 0; index < generatorPolynom.PolyItems.Count; index++) - generatorPolynom.PolyItems[index] = new PolynomItem(generatorPolynom.PolyItems[index].Coefficient, - generatorPolynom.PolyItems[index].Exponent + (messagePolynom.PolyItems.Count - 1)); - - var leadTermSource = messagePolynom; - var i = 0; - while (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0) - { - if (leadTermSource.PolyItems[0].Coefficient == 0) + private static bool MatchesFinderPattern(QRCodeData qrCode, int x, int y, bool vertical = false) { - leadTermSource.PolyItems.RemoveAt(0); - leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); + if (vertical) + { + return (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]); + } + + return (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]); } - else + + private static int ScoreBalance(QRCodeData qrCode, int size) { - var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); - resPoly = ConvertToDecNotation(resPoly); - resPoly = XORPolynoms(leadTermSource, resPoly); - leadTermSource = resPoly; + var blackModules = qrCode.ModuleMatrix.Sum(row => row.Cast().Count(x => x)); + var percent = ((double)blackModules / (size * size)) * 100; + var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5d) * 5 - 50) / 5; + var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5d) * 5 - 45) / 5; + return Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; } - i++; - } - return leadTermSource.PolyItems.Select(x => DecToBin(x.Coefficient, 8)).ToList(); - } - private static Polynom ConvertToAlphaNotation(Polynom poly) - { - var newPoly = new Polynom(); - for (var i = 0; i < poly.PolyItems.Count; i++) - newPoly.PolyItems.Add( - new PolynomItem( - (poly.PolyItems[i].Coefficient != 0 - ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) - : 0), poly.PolyItems[i].Exponent)); - return newPoly; - } - - private static Polynom ConvertToDecNotation(Polynom poly) - { - var newPoly = new Polynom(); - for (var i = 0; i < poly.PolyItems.Count; i++) - newPoly.PolyItems.Add(new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent)); - return newPoly; - } - - private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) - { - var fittingVersions = capacityTable.Where( - x => x.Details.Any( - y => (y.ErrorCorrectionLevel == eccLevel - && y.CapacityDict[encMode] >= Convert.ToInt32(length) - ) - ) - ).Select(x => new - { - version = x.Version, - capacity = x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel) - .CapacityDict[encMode] - }); - - if (fittingVersions.Any()) - return fittingVersions.Min(x => x.version); - - 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 DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); - } - - private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) - { - 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 - } - - private static bool IsInRange(char c, char min, char max) - { - return (uint)(c - min) <= (uint)(max - min); - } - - private static Polynom CalculateMessagePolynom(string bitString) - { - var messagePol = new Polynom(); - for (var i = bitString.Length / 8 - 1; i >= 0; i--) - { - messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); - bitString = bitString.Remove(0, 8); - } - return messagePol; - } - - private static Polynom CalculateGeneratorPolynom(int numEccWords) - { - var generatorPolynom = new Polynom(); - generatorPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(0,0) - }); - for (var i = 1; i <= numEccWords - 1; i++) - { - var multiplierPolynom = new Polynom(); - multiplierPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(i,0) - }); - - generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); - } - - return generatorPolynom; - } - - private static List BinaryStringToBitBlockList(string bitString) - { - const int blockSize = 8; - var numberOfBlocks = (int)Math.Ceiling(bitString.Length / (double)blockSize); - var blocklist = new List(numberOfBlocks); - - for (int i = 0; i < bitString.Length; i += blockSize) - { - blocklist.Add(bitString.Substring(i, blockSize)); } - - return blocklist; } - private static List BinaryStringListToDecList(List binaryStringList) - { - return binaryStringList.Select(binaryString => BinToDec(binaryString)).ToList(); - } - - private static int BinToDec(string binStr) - { - return Convert.ToInt32(binStr, 2); - } - - private static string DecToBin(int decNum) - { - return Convert.ToString(decNum, 2); - } - - private static string DecToBin(int decNum, int padLeftUpTo) - { - var binStr = DecToBin(decNum); - return binStr.PadLeft(padLeftUpTo, '0'); - } private static int GetCountIndicatorLength(int version, EncodingMode encMode) { - 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; - } - else + var versionGroup = version < 10 ? 0 : version < 27 ? 1 : 2; + switch (encMode) { - if (encMode == EncodingMode.Numeric) - return 14; - else if (encMode == EncodingMode.Alphanumeric) - return 13; - else if (encMode == EncodingMode.Byte) - return 16; - else - return 12; + case EncodingMode.Numeric: + return new[] { 10, 12, 14 }[versionGroup]; + case EncodingMode.Alphanumeric: + return new[] { 9, 11, 13 }[versionGroup]; + case EncodingMode.Byte: + return new[] { 8, 16, 16 }[versionGroup]; + default: + return new[] { 8, 10, 12 }[versionGroup]; } } @@ -1143,6 +927,171 @@ private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, b return codeText; } + private static Polynom ConvertToAlphaNotation(Polynom poly) + { + var newPoly = new Polynom(); + for (var i = 0; i < poly.PolyItems.Count; i++) + newPoly.PolyItems.Add( + new PolynomItem( + poly.PolyItems[i].Coefficient != 0 ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) : 0, + poly.PolyItems[i].Exponent)); + return newPoly; + } + + private static Polynom ConvertToDecNotation(Polynom poly) + { + var newPoly = new Polynom(); + for (var i = 0; i < poly.PolyItems.Count; i++) + newPoly.PolyItems.Add(new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent)); + return newPoly; + } + + private static Polynom CalculateMessagePolynom(string bitString) + { + var messagePol = new Polynom(); + for (var i = bitString.Length / 8 - 1; i >= 0; i--) + { + messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); + bitString = bitString.Remove(0, 8); + } + return messagePol; + } + + private static Polynom CalculateGeneratorPolynom(int numEccWords) + { + var generatorPolynom = new Polynom(); + generatorPolynom.PolyItems.AddRange(new[] + { + new PolynomItem(0, 1), + new PolynomItem(0, 0) + }); + for (var i = 1; i <= numEccWords - 1; i++) + { + var multiplierPolynom = new Polynom(); + multiplierPolynom.PolyItems.AddRange(new[] + { + new PolynomItem(0, 1), + new PolynomItem(i, 0) + }); + + generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); + } + + return generatorPolynom; + } + + private static List BinaryStringToBitBlockList(string bitString) + { + const int blockSize = 8; + var numberOfBlocks = (int)Math.Ceiling(bitString.Length / (double)blockSize); + var blocklist = new List(numberOfBlocks); + + for (int i = 0; i < bitString.Length; i += blockSize) + { + blocklist.Add(bitString.Substring(i, blockSize)); + } + + return blocklist; + } + + private static List BinaryStringListToDecList(List binaryStringList) + { + return binaryStringList.Select(binaryString => BinToDec(binaryString)).ToList(); + } + + private static List CalculateECCWords(string bitString, EccInfo eccInfo) + { + var eccWords = eccInfo.EccPerBlock; + var messagePolynom = CalculateMessagePolynom(bitString); + var generatorPolynom = CalculateGeneratorPolynom(eccWords); + + for (var i = 0; i < messagePolynom.PolyItems.Count; i++) + messagePolynom.PolyItems[i] = new PolynomItem(messagePolynom.PolyItems[i].Coefficient, + messagePolynom.PolyItems[i].Exponent + eccWords); + + for (var i = 0; i < generatorPolynom.PolyItems.Count; i++) + generatorPolynom.PolyItems[i] = new PolynomItem(generatorPolynom.PolyItems[i].Coefficient, + generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count - 1)); + + var leadTermSource = messagePolynom; + for (var i = 0; (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0); i++) + { + if (leadTermSource.PolyItems[0].Coefficient == 0) + { + leadTermSource.PolyItems.RemoveAt(0); + leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); + } + else + { + var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); + resPoly = ConvertToDecNotation(resPoly); + resPoly = XORPolynoms(leadTermSource, resPoly); + leadTermSource = resPoly; + } + } + return leadTermSource.PolyItems.Select(x => DecToBin(x.Coefficient, 8)).ToList(); + } + + private static int BinToDec(string binStr) + { + return Convert.ToInt32(binStr, 2); + } + + private static string DecToBin(int decNum) + { + return Convert.ToString(decNum, 2); + } + + private static string DecToBin(int decNum, int padLeftUpTo) + { + var binStr = DecToBin(decNum); + return binStr.PadLeft(padLeftUpTo, '0'); + } + + private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) + { + var fittingVersions = capacityTable.Where( + x => x.Details.Any( + y => (y.ErrorCorrectionLevel == eccLevel + && y.CapacityDict[encMode] >= Convert.ToInt32(length) + ) + ) + ).Select(x => new + { + version = x.Version, + capacity = x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel) + .CapacityDict[encMode] + }); + + if (fittingVersions.Any()) + return fittingVersions.Min(x => x.version); + + 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 DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); + } + + private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) + { + if (forceUtf8) return EncodingMode.Byte; + EncodingMode result = EncodingMode.Numeric; + foreach (char c in plainText) + { + if (IsInRange(c, '0', '9')) continue; + result = EncodingMode.Alphanumeric; + if (IsInRange(c, 'A', 'Z') || alphanumEncTable.Contains(c)) continue; + return EncodingMode.Byte; + } + return result; + } + + private static bool IsInRange(char c, char min, char max) + { + return (uint)(c - min) <= (uint)(max - min); + } + private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) { var resultPolynom = new Polynom(); @@ -1256,31 +1205,32 @@ private static List CreateAlignmentPatternTable() for (var i = 0; i < (7 * 40); i = i + 7) { - var points = new List(); - for (var x = 0; x < 7; x++) + localAlignmentPatternTable.Add(new AlignmentPattern { - if (alignmentPatternBaseValues[i + x] != 0) - { - for (var y = 0; y < 7; y++) - { - if (alignmentPatternBaseValues[i + y] != 0) - { - var p = new Point(alignmentPatternBaseValues[i + x] - 2, alignmentPatternBaseValues[i + y] - 2); - if (!points.Contains(p)) - points.Add(p); - } - } - } - } + Version = (i + 7) / 7, + PatternPositions = CreateAlignmentPatternPoints(i) + }); + } + return localAlignmentPatternTable; + } - localAlignmentPatternTable.Add(new AlignmentPattern() + private static List CreateAlignmentPatternPoints(int offset) + { + var points = new List(); + for (var x = 0; x < 7; x++) + { + if (alignmentPatternBaseValues[offset + x] == 0) + continue; + for (var y = 0; y < 7; y++) { - Version = (i + 7) / 7, - PatternPositions = points + if (alignmentPatternBaseValues[offset + y] == 0) + continue; + var point = new Point(alignmentPatternBaseValues[offset + x] - 2, alignmentPatternBaseValues[offset + y] - 2); + if (!points.Contains(point)) + points.Add(point); } - ); } - return localAlignmentPatternTable; + return points; } private static List CreateCapacityECCTable() @@ -1599,12 +1549,7 @@ public SKRectI(int x, int y, int w, int h) /// public void Dispose() { - if (this.disposed) - { - return; - } - - this.disposed = true; + GC.SuppressFinalize(this); } } }