diff --git a/Source/GenCode128/Code128Code.cs b/Source/GenCode128/Code128Code.cs index edc41ed..46247f9 100644 --- a/Source/GenCode128/Code128Code.cs +++ b/Source/GenCode128/Code128Code.cs @@ -1,20 +1,19 @@ namespace GenCode128 { + using System; + /// /// Static tools for determining codes for individual characters in the content /// public static class Code128Code { private const int CShift = 98; - private const int CCodeA = 101; - private const int CCodeB = 100; - + private const int CCodeC = 99; private const int CStartA = 103; - private const int CStartB = 104; - + private const int CStartC = 105; private const int CStop = 106; /// @@ -23,15 +22,14 @@ public static class Code128Code public enum CodeSetAllowed { CodeA, - CodeB, - - CodeAorB + CodeC, + CodeAorB, + CodeAorBorC } /// - /// Get the Code128 code value(s) to represent an ASCII character, with - /// optional look-ahead for length optimization + /// Get the Code128 code value(s) to represent an ASCII character /// /// The ASCII value of the character to translate /// The next character in sequence (or -1 if none) @@ -39,51 +37,120 @@ public enum CodeSetAllowed /// if the returned codes change that, then this value will be changed to reflect it /// An array of integers representing the codes that need to be output to produce the /// given character - public static int[] CodesForChar(int charAscii, int lookAheadAscii, ref CodeSet currentCodeSet) + public static int[] CodesForChar(ref int[] charAscii, ref CodeSet currentCodeSet, ref int outerIndex) { int[] result; var shifter = -1; + var whatToConvert = -1; + CodeSet bestCodeSet; + + // Let's find out which is the best set to use + bestCodeSet = BestFitSet(ref charAscii, currentCodeSet, ref shifter); - if (!CharCompatibleWithCodeset(charAscii, currentCodeSet)) + // We`re not in the right CodeSet, so we should change it + if (currentCodeSet != bestCodeSet) { - // if we have a lookahead character AND if the next character is ALSO not compatible - if ((lookAheadAscii != -1) && !CharCompatibleWithCodeset(lookAheadAscii, currentCodeSet)) + switch (bestCodeSet) { - // we need to switch code sets - switch (currentCodeSet) - { - case CodeSet.CodeA: - shifter = CCodeB; - currentCodeSet = CodeSet.CodeB; - break; - case CodeSet.CodeB: - shifter = CCodeA; - currentCodeSet = CodeSet.CodeA; - break; - } - } - else - { - // no need to switch code sets, a temporary SHIFT will suffice - shifter = CShift; + case CodeSet.CodeA: + shifter = CCodeA; + currentCodeSet = CodeSet.CodeA; + break; + case CodeSet.CodeB: + shifter = CCodeB; + currentCodeSet = CodeSet.CodeB; + break; + case CodeSet.CodeC: + shifter = CCodeC; + currentCodeSet = CodeSet.CodeC; + break; } } + // Check if is A/B or C and fill convert, but if it`s CodeSetC, we should code 2 at a time, instead of just one + switch (currentCodeSet) + { + case CodeSet.CodeA: + case CodeSet.CodeB: + whatToConvert = charAscii[0]; + break; + case CodeSet.CodeC: + whatToConvert = (Convert.ToInt32(Char.ConvertFromUtf32(charAscii[0])) * 10) + (Convert.ToInt32(Char.ConvertFromUtf32(charAscii[1]))); + outerIndex++; + break; + } + if (shifter != -1) { result = new int[2]; result[0] = shifter; - result[1] = CodeValueForChar(charAscii); + result[1] = CodeValueForChar(whatToConvert, currentCodeSet); } else { result = new int[1]; - result[0] = CodeValueForChar(charAscii); + result[0] = CodeValueForChar(whatToConvert, currentCodeSet); } return result; } + /// + /// Determines the best codeset to use, based on Code128 heuristics and optimizations + /// + /// characters to check for + /// codeset context to test + /// a reference for the shifter + /// the best codeset to be used. Shift is also modified if necessary. + public static CodeSet BestFitSet(ref int[] charAscii, CodeSet currentCodeSet, ref int shifter) + { + bool bestA = false; + bool bestC = false; + bool commingFromC = false; + //bool bestB = false; + + // Optimizing to use CodeSetC. If 6+ are numbers OR we're in the last 4 of the set, and all are numbers + // Care that we run from 0 to 5 -- Running through 6 and also checking >=3 which means the 4th ahead + if (currentCodeSet != CodeSet.CodeC) + { + bestC = true; + for (int i = 0; i < 6; i++) + { + bestC = bestC && (CharCompatibleWithCodeset(charAscii[i], CodeSet.CodeC) ? ((charAscii[4] != -1 && charAscii[5] == -1) ? false : true) : ((charAscii[i] == -1 && i > 3) ? true : false)); + if (!bestC) + break; + } + } + else + { + // if we're already in C, let's check if we can code at least 2 ascii + if (CharCompatibleWithCodeset(charAscii[0], CodeSet.CodeC) && CharCompatibleWithCodeset(charAscii[1], CodeSet.CodeC)) + { + bestC = true; + } + commingFromC = true; + } + + if (CharCompatibleWithCodeset(charAscii[0], CodeSet.CodeA)) + { + // if we have a lookahead character AND if the next character is ALSO not compatible + if ((charAscii[1] != -1) && (commingFromC || CharCompatibleWithCodeset(charAscii[1], CodeSet.CodeA))) + { + // we need to switch code sets + bestA = true; + } + else + { + // no need to switch code sets, a temporary SHIFT will suffice + shifter = CShift; + } + } + + //Since we've already checked C and A, if both are False, we can only use CodeSetB + // Select the prefered one, putting CodeSetC as the first, since it's optimized + return bestC ? CodeSet.CodeC : (bestA ? CodeSet.CodeA : CodeSet.CodeB); + } + /// /// Tells us which codesets a given character value is allowed in /// @@ -91,13 +158,17 @@ public static int[] CodesForChar(int charAscii, int lookAheadAscii, ref CodeSet /// Which codeset(s) can be used to represent this character public static CodeSetAllowed CodesetAllowedForChar(int charAscii) { - if (charAscii >= 32 && charAscii <= 95) + if (charAscii >= 48 && charAscii <= 57) + { + return CodeSetAllowed.CodeC; + } + else if (charAscii >= 32 && charAscii <= 95) { return CodeSetAllowed.CodeAorB; } else { - return charAscii < 32 ? CodeSetAllowed.CodeA : CodeSetAllowed.CodeB; + return (charAscii < 32) ? CodeSetAllowed.CodeA : CodeSetAllowed.CodeB; } } @@ -110,7 +181,9 @@ public static CodeSetAllowed CodesetAllowedForChar(int charAscii) public static bool CharCompatibleWithCodeset(int charAscii, CodeSet currentCodeSet) { var csa = CodesetAllowedForChar(charAscii); - return csa == CodeSetAllowed.CodeAorB || (csa == CodeSetAllowed.CodeA && currentCodeSet == CodeSet.CodeA) + return (csa == CodeSetAllowed.CodeC && currentCodeSet == CodeSet.CodeC) + || (csa == CodeSetAllowed.CodeAorB && (currentCodeSet == CodeSet.CodeA || currentCodeSet == CodeSet.CodeB)) + || (csa == CodeSetAllowed.CodeA && currentCodeSet == CodeSet.CodeA) || (csa == CodeSetAllowed.CodeB && currentCodeSet == CodeSet.CodeB); } @@ -119,9 +192,9 @@ public static bool CharCompatibleWithCodeset(int charAscii, CodeSet currentCodeS /// /// character to convert /// code128 symbol value for the character - public static int CodeValueForChar(int charAscii) + public static int CodeValueForChar(int charAscii, CodeSet currentCodeSet) { - return charAscii >= 32 ? charAscii - 32 : charAscii + 64; + return currentCodeSet == CodeSet.CodeC ? charAscii : ((charAscii >= 32) ? charAscii - 32 : charAscii + 64); } /// @@ -131,7 +204,7 @@ public static int CodeValueForChar(int charAscii) /// The code128 code to start a barcode in that codeset public static int StartCodeForCodeSet(CodeSet cs) { - return cs == CodeSet.CodeA ? CStartA : CStartB; + return cs == CodeSet.CodeA ? CStartA : (cs == CodeSet.CodeB ? CStartB : CStartC); } /// diff --git a/Source/GenCode128/Code128Content.cs b/Source/GenCode128/Code128Content.cs index 1ef32b6..8a98871 100644 --- a/Source/GenCode128/Code128Content.cs +++ b/Source/GenCode128/Code128Content.cs @@ -32,27 +32,44 @@ private int[] StringToCode128(string asciiData) { // turn the string into ascii byte data var asciiBytes = Encoding.ASCII.GetBytes(asciiData); + + // a support array, for our lookahead + int[] nextchar = new int[6]; + + // a support array for us to choose which is the best starting code + Code128Code.CodeSetAllowed[] csa = new Code128Code.CodeSetAllowed[4]; // decide which codeset to start with - var csa1 = asciiBytes.Length > 0 - ? Code128Code.CodesetAllowedForChar(asciiBytes[0]) - : Code128Code.CodeSetAllowed.CodeAorB; - var csa2 = asciiBytes.Length > 1 - ? Code128Code.CodesetAllowedForChar(asciiBytes[1]) - : Code128Code.CodeSetAllowed.CodeAorB; - var currentCodeSet = this.GetBestStartSet(csa1, csa2); + + csa[0] = asciiBytes.Length > 0 + ? Code128Code.CodesetAllowedForChar(asciiBytes[0]) + : Code128Code.CodeSetAllowed.CodeAorBorC; + csa[1] = asciiBytes.Length > 1 + ? Code128Code.CodesetAllowedForChar(asciiBytes[1]) + : Code128Code.CodeSetAllowed.CodeAorBorC; + csa[2] = asciiBytes.Length > 2 + ? Code128Code.CodesetAllowedForChar(asciiBytes[2]) + : Code128Code.CodeSetAllowed.CodeAorBorC; + csa[3] = asciiBytes.Length > 3 + ? Code128Code.CodesetAllowedForChar(asciiBytes[3]) + : Code128Code.CodeSetAllowed.CodeAorBorC; + var currentCodeSet = this.GetBestStartSet(csa); // set up the beginning of the barcode // assume no codeset changes, account for start, checksum, and stop var codes = new ArrayList(asciiBytes.Length + 3) { Code128Code.StartCodeForCodeSet(currentCodeSet) }; - - // add the codes for each character in the string + + // add the codes for each character in the string, checking the 6 char lookahead for (var i = 0; i < asciiBytes.Length; i++) { - int thischar = asciiBytes[i]; - var nextchar = asciiBytes.Length > i + 1 ? asciiBytes[i + 1] : -1; + nextchar[0] = asciiBytes[i]; + nextchar[1] = asciiBytes.Length > (i + 1) ? asciiBytes[i + 1] : -1; + nextchar[2] = asciiBytes.Length > (i + 2) ? asciiBytes[i + 2] : -1; + nextchar[3] = asciiBytes.Length > (i + 3) ? asciiBytes[i + 3] : -1; + nextchar[4] = asciiBytes.Length > (i + 4) ? asciiBytes[i + 4] : -1; + nextchar[5] = asciiBytes.Length > (i + 5) ? asciiBytes[i + 5] : -1; - codes.AddRange(Code128Code.CodesForChar(thischar, nextchar, ref currentCodeSet)); + codes.AddRange(Code128Code.CodesForChar(ref nextchar, ref currentCodeSet, ref i)); } // calculate the check digit @@ -71,22 +88,36 @@ private int[] StringToCode128(string asciiData) } /// - /// Determines the best starting code set based on the the first two + /// Determines the best starting code set based on the the /// characters of the string to be encoded /// - /// First character of input string - /// Second character of input string + /// First 4 characters of input string /// The codeset determined to be best to start with - private CodeSet GetBestStartSet(Code128Code.CodeSetAllowed csa1, Code128Code.CodeSetAllowed csa2) + private CodeSet GetBestStartSet(Code128Code.CodeSetAllowed[] initChars) { - var vote = 0; + int vote = 0; + int votec = 0; - vote += csa1 == Code128Code.CodeSetAllowed.CodeA ? 1 : 0; - vote += csa1 == Code128Code.CodeSetAllowed.CodeB ? -1 : 0; - vote += csa2 == Code128Code.CodeSetAllowed.CodeA ? 1 : 0; - vote += csa2 == Code128Code.CodeSetAllowed.CodeB ? -1 : 0; + for (int i = 0; i < 4; i++) + { + switch (initChars[i]) + { + case Code128Code.CodeSetAllowed.CodeA: + vote += 1; + break; + case Code128Code.CodeSetAllowed.CodeB: + vote -= 1; + break; + case Code128Code.CodeSetAllowed.CodeC: + votec += 2; + break; + /*default: + break;*/ + } + } - return vote > 0 ? CodeSet.CodeA : CodeSet.CodeB; // ties go to codeB due to my own prejudices + //Optimizing: 4+ or only 2 characters codeC. Otherwise, vote for codeA or codeB. Ties go to codeB due to my own prejudices + return (votec == 8 || (votec == 4 && vote == 0)) ? CodeSet.CodeC : ((vote > 0) ? CodeSet.CodeA : CodeSet.CodeB); } } } diff --git a/Source/GenCode128/Code128Rendering.cs b/Source/GenCode128/Code128Rendering.cs index 0dd7fc1..20944dd 100644 --- a/Source/GenCode128/Code128Rendering.cs +++ b/Source/GenCode128/Code128Rendering.cs @@ -15,6 +15,9 @@ public static class Code128Rendering // however, the last one -- STOP -- has 7. The cost of the // extra integers is trivial, and this lets the code flow // much more elegantly + // The code representes 1s and 0s, that always change and, + // this way, we can representa all with 6 elements. + // Reference: http://www.barcodeisland.com/code128.phtml private static readonly int[,] CPatterns = { { 2, 1, 2, 2, 2, 2, 0, 0 }, // 0 diff --git a/Source/GenCode128/CodeSet.cs b/Source/GenCode128/CodeSet.cs index 07c7bd1..e56bbcc 100644 --- a/Source/GenCode128/CodeSet.cs +++ b/Source/GenCode128/CodeSet.cs @@ -3,7 +3,7 @@ namespace GenCode128 public enum CodeSet { CodeA, - CodeB - //// CodeC // not supported + CodeB, + CodeC } } \ No newline at end of file