Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code 128 C Implemented #2

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 112 additions & 39 deletions Source/GenCode128/Code128Code.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
namespace GenCode128
{
using System;

/// <summary>
/// Static tools for determining codes for individual characters in the content
/// </summary>
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;

/// <summary>
Expand All @@ -23,81 +22,153 @@ public static class Code128Code
public enum CodeSetAllowed
{
CodeA,

CodeB,

CodeAorB
CodeC,
CodeAorB,
CodeAorBorC
}

/// <summary>
/// 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
/// </summary>
/// <param name="charAscii">The ASCII value of the character to translate</param>
/// <param name="lookAheadAscii">The next character in sequence (or -1 if none)</param>
/// <param name="currentCodeSet">The current codeset, that the returned codes need to follow;
/// if the returned codes change that, then this value will be changed to reflect it</param>
/// <returns>An array of integers representing the codes that need to be output to produce the
/// given character</returns>
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;
}

/// <summary>
/// Determines the best codeset to use, based on Code128 heuristics and optimizations
/// </summary>
/// <param name="charAscii">characters to check for</param>
/// <param name="currentCodeSet">codeset context to test</param>
/// <param name="shifter">a reference for the shifter</param>
/// <returns>the best codeset to be used. Shift is also modified if necessary.</returns>
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);
}

/// <summary>
/// Tells us which codesets a given character value is allowed in
/// </summary>
/// <param name="charAscii">ASCII value of character to look at</param>
/// <returns>Which codeset(s) can be used to represent this character</returns>
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;
}
}

Expand All @@ -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);
}

Expand All @@ -119,9 +192,9 @@ public static bool CharCompatibleWithCodeset(int charAscii, CodeSet currentCodeS
/// </summary>
/// <param name="charAscii">character to convert</param>
/// <returns>code128 symbol value for the character</returns>
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);
}

/// <summary>
Expand All @@ -131,7 +204,7 @@ public static int CodeValueForChar(int charAscii)
/// <returns>The code128 code to start a barcode in that codeset</returns>
public static int StartCodeForCodeSet(CodeSet cs)
{
return cs == CodeSet.CodeA ? CStartA : CStartB;
return cs == CodeSet.CodeA ? CStartA : (cs == CodeSet.CodeB ? CStartB : CStartC);
}

/// <summary>
Expand Down
75 changes: 53 additions & 22 deletions Source/GenCode128/Code128Content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -71,22 +88,36 @@ private int[] StringToCode128(string asciiData)
}

/// <summary>
/// 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
/// </summary>
/// <param name="csa1">First character of input string</param>
/// <param name="csa2">Second character of input string</param>
/// <param name="initChars">First 4 characters of input string</param>
/// <returns>The codeset determined to be best to start with</returns>
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);
}
}
}
3 changes: 3 additions & 0 deletions Source/GenCode128/Code128Rendering.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Source/GenCode128/CodeSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace GenCode128
public enum CodeSet
{
CodeA,
CodeB
//// CodeC // not supported
CodeB,
CodeC
}
}