diff --git a/Barcode Writer/1D/Code128.cs b/Barcode Writer/1D/Code128.cs index 884aab4..295dff3 100644 --- a/Barcode Writer/1D/Code128.cs +++ b/Barcode Writer/1D/Code128.cs @@ -10,7 +10,7 @@ namespace Barcodes /// public class Code128 : BarcodeBase { - private const int STOP = 106; + private readonly int STOP = Code128Helper.STOP; protected int AiMarker = 156; protected override void Init() @@ -172,7 +172,7 @@ private Pattern ParseCode(string value) protected override string ParseText(string value, CodedValueCollection codes) { - char variant = Code128Helper.StartVariantB; + char variant = Code128Helper.CODEB; int i = 0; bool shifted = false; @@ -373,8 +373,28 @@ protected string AutoFormat(string value) } else { - v = Code128Helper.CODEB; - s.Append(Code128Helper.StartVariantB); + switch (value[0]) + { + case Code128Helper.StartVariantA: + v = Code128Helper.CODEA; + s.Append(Code128Helper.StartVariantA); + ++i; + break; + case Code128Helper.StartVariantC: + v = Code128Helper.CODEC; + s.Append(Code128Helper.StartVariantC); + ++i; + break; + case Code128Helper.StartVariantB: + v = Code128Helper.CODEB; + s.Append(Code128Helper.StartVariantB); + ++i; + break; + default: + v = Code128Helper.CODEB; + s.Append(Code128Helper.StartVariantB); + break; + } } bool switched = false; @@ -416,7 +436,7 @@ protected string AutoFormat(string value) s.Append(value[i]); i++; - if (v == Code128Helper.CODEC) + if (i < value.Length && v == Code128Helper.CODEC) { s.Append(value[i]); i++; diff --git a/Barcode Writer/1D/Code128Helper.cs b/Barcode Writer/1D/Code128Helper.cs index 192fee5..2a3a2ff 100644 --- a/Barcode Writer/1D/Code128Helper.cs +++ b/Barcode Writer/1D/Code128Helper.cs @@ -53,5 +53,10 @@ public static class Code128Helper /// Shifts the next value between A & B /// public const char SHIFT = (char)148; + + /// + /// Stop character + /// + public const int STOP = 106; } } diff --git a/Barcode Writer/1D/EAN128.cs b/Barcode Writer/1D/EAN128.cs index a29950c..7421cb2 100644 --- a/Barcode Writer/1D/EAN128.cs +++ b/Barcode Writer/1D/EAN128.cs @@ -59,15 +59,55 @@ public Bitmap Generate(GS1.GS1Builder value, BarcodeSettings settings) protected override string ParseText(string value, CodedValueCollection codes) { - value = base.ParseText(value, codes); + if (_Value == null) + { + return base.ParseText(value, codes); + } + else + { + if (_Value.ToString() != value) + base.ParseText(value, codes); + else + { + int i = 0; + foreach(var element_string in _Value.CalculateElementStrings()) + { + var cds = new CodedValueCollection(); + if (!element_string.EndsWith(char.ToString(Code128Helper.FNC1))) + { + base.ParseText(element_string, cds); + cds.RemoveAt(cds.Count - 1); + } + else + { + base.ParseText(element_string.Substring(0, element_string.Length - 1), cds); + cds.RemoveAt(cds.Count - 1); + cds.Add(Code128Helper.FNC1 - 50); + } - if (_Value != null) - { - value = _Value.ToDisplayString(); - _Value = null; - } + if (i == 0) + cds.Insert(1, Code128Helper.FNC1 - 50); + else if (codes[0] == cds[0]) + cds.RemoveAt(0); + else + { + var r = cds[0]; + cds.RemoveAt(0); + var ai = _Value.AICollection[i].ToString("{0:00}"); + cds.Insert(ai.Length, r); + } + + codes.AddRange(cds); + ++i; + } + codes.Add(Code128Helper.STOP); + } + + var ret = _Value.ToDisplayString(); + _Value = null; + return ret; + } - return value; } //public string CreateEAN128(int aiCode, string value) diff --git a/Barcode Writer/GS1/GS1Builder.cs b/Barcode Writer/GS1/GS1Builder.cs index b08bcd6..ba190c8 100644 --- a/Barcode Writer/GS1/GS1Builder.cs +++ b/Barcode Writer/GS1/GS1Builder.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Text; +using System.Linq; namespace Barcodes.GS1 { - public class GS1Builder + public class GS1Builder { #region AI Constants @@ -29,6 +30,7 @@ public class GS1Builder public const int ReferenceToSourceEntity = 251; public const int GDTI = 253; public const int GLNExtension = 254; + public const int GlobalCouponNumber = 255; public const int CountofItems = 30; public const int ProductNetWeightKg = 310; public const int ProductLengthMeters = 311; @@ -109,6 +111,7 @@ public class GS1Builder public const int UNCutClassification = 7002; public const int ExpirationDateTime = 7003; public const int ActivePotency = 7004; + public const int ProcessorApproval = 703; public const int RollProducts = 8001; public const int CellularMobileID = 8002; public const int GRAI = 8003; @@ -123,6 +126,7 @@ public class GS1Builder public const int CouponExtendedCodeEndofOffer = 8101; public const int CouponExtendedCode0 = 8102; public const int CouponCodeIDNorthAmerica = 8110; + public const int ExtendedPackagingURL = 8200; public const int TradingPartners = 90; public const int InternalCompanyCodes1 = 91; public const int InternalCompanyCodes2 = 92; @@ -166,6 +170,56 @@ public GS1Builder(char fnc1) FNC1 = fnc1; } + private bool IsVariable(int ai) + { + return ai == BatchNumbers || + ai == SerialNumber || + ai == HIBCCQuantity || + ai == SecondaryDataField || + ai == LotNumber || + ai == AdditionalItemIdentification || + ai == CustomerPartNumber || + ai == MadeToOrderVariationNumber || + ai == SecondSerialNumber || + ai == ReferenceToSourceEntity || + ai == GDTI || + ai == GLNExtension || + ai == GlobalCouponNumber || + ai == CountofItems || + ai == CountofTradeItems || + ai == AmountPayable || + ai == AmountPayableISO || + ai == AmountPayableArea || + ai == AmountPayableAreaISO || + ai == CustomerPurchaseOrderNumber || + ai == GINC || + ai == RoutingCode || + ai == ShipToPostalCode || + ai == ShipToPostalCodeISO || + ai == CountryOfInitialProcessing || + ai == UNCutClassification || + ai == ActivePotency || + ai == ProcessorApproval || + ai == CellularMobileID || + ai == GRAI || + ai == GIAI || + ai == IBAN || + ai == ProductionDateTime || + ai == PaymentSlipReference || + ai == CouponCodeIDNorthAmerica || + ai == ExtendedPackagingURL || + ai == TradingPartners || + ai == InternalCompanyCodes1 || + ai == InternalCompanyCodes2 || + ai == InternalCompanyCodes3 || + ai == InternalCompanyCodes4 || + ai == InternalCompanyCodes5 || + ai == InternalCompanyCoeds6 || + ai == InternalCompanyCodes7 || + ai == InternalCompanyCodes8 || + ai == InternalCompanyCodes9; + } + public void Add(int ai, string value) { Validate(ai, value); @@ -413,14 +467,12 @@ public override string ToString() { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < _Ais.Count; i++) - { - sb.Append(FNC1); - sb.Append(_Ais[i]); - sb.Append(_Values[i]); - } + sb.Append(FNC1); + CalculateElementStrings() + .ToList() + .ForEach((v) => sb.Append(v)); - return sb.ToString(); + return sb.ToString(); } public string ToString(char fnc1) @@ -435,11 +487,23 @@ public string ToDisplayString() for (int i = 0; i < _Ais.Count; i++) { - sb.AppendFormat("({0})", _Ais[i]); + sb.AppendFormat("({0:00})", _Ais[i]); sb.Append(_Values[i]); } return sb.ToString(); } - } + public IEnumerable CalculateElementStrings() + { + for (int i = 0; i < _Ais.Count; i++) + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat("{0:00}", _Ais[i]); + sb.Append(_Values[i]); + if (IsVariable(_Ais[i])) + sb.Append(FNC1); + yield return sb.ToString(); + } + } + } } diff --git a/BarcodeWriterTests/BarcodeWriterTests.csproj b/BarcodeWriterTests/BarcodeWriterTests.csproj new file mode 100644 index 0000000..baae746 --- /dev/null +++ b/BarcodeWriterTests/BarcodeWriterTests.csproj @@ -0,0 +1,98 @@ + + + + Debug + AnyCPU + {D694DBF2-F65C-4C92-9ACC-629A40214D7D} + Library + Properties + BarcodeWriterTests + BarcodeWriterTests + v4.5.2 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + False + + + + + + + + + + + + + + {7130d5a2-8818-4300-9d1a-80c929059a2d} + Barcode Writer + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/BarcodeWriterTests/Code128Test.cs b/BarcodeWriterTests/Code128Test.cs new file mode 100644 index 0000000..da7025a --- /dev/null +++ b/BarcodeWriterTests/Code128Test.cs @@ -0,0 +1,42 @@ +using Barcodes; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Drawing; +using BarcodeWriterTests.Helpers; +using System.Linq; +using System; + +namespace BarcodeWriterTests +{ + [TestClass] + public class Code128Test + { + [TestMethod] + public void Code128_Constructor() + { + var b = new Code128(); + Assert.IsInstanceOfType(value: b, expectedType: typeof(Code128), message: "fail constructor Code128"); + } + + [TestMethod] + public void Code128_IsValidData() + { + var b = new Code128(); + const string s = "\x01 09azAZ\t\n\x7f"; + Assert.IsTrue(condition: b.IsValidData(s), message: "fail IsValidData()"); + } + + [TestMethod] + public void Code128_Genelate() + { + + var target = new Code128(); + var text = "9784569809731"; + using (var actual_image = target.Generate(text: text)) + { + var expected = "69ecc3d51fa5fc1c4a7fb2290d5a73ef920e2bbc4638da5d0e5e2856d1f27c1"; + var actual = string.Join("", actual_image.ComputeHash().Select(_ => Convert.ToString(_, 16))); + Assert.AreEqual(expected: expected, actual: actual, message: "fail Code128.Generate({0})", parameters: text); + } + } + } +} diff --git a/BarcodeWriterTests/EAN128Test.cs b/BarcodeWriterTests/EAN128Test.cs new file mode 100644 index 0000000..35b8828 --- /dev/null +++ b/BarcodeWriterTests/EAN128Test.cs @@ -0,0 +1,66 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using Barcodes; +using Barcodes.GS1; +using System.Drawing; +using BarcodeWriterTests.Helpers; +using System.Linq; + +namespace BarcodeWriterTests +{ + [TestClass] + public class EAN128Test + { + [TestMethod] + public void EAN128_GenerateWithGS1Builder() + { + var builder = new GS1Builder(); + var data = new[] { + new { ai=01, Value="14912345678904" }, + new { ai=17, Value="990101" }, + new { ai=30, Value="1000" }, + new { ai=10, Value="1234" }, + new { ai=98, Value="0007" + "0448" + "101110" + "200000" + "2" + "0000" }, + }; + data.ToList().ForEach(d => builder.Add(d.ai, d.Value)); + var target = new EAN128(); + var expected_filepath = string.Format("EAN128_{0}.bmp", string.Join( + string.Empty, data.Select(v => string.Format("{0:00}{1}", v.ai, v.Value)) + )); + using (var actual_image = target.Generate(value: builder)) + { + var expected = "9e8ff7c269aee9fdd967f1a598c8de38e1c85f77084158fea9dc7a25549a72"; + var actual = string.Join("", actual_image.ComputeHash().Select(_ => Convert.ToString(_, 16))); + actual_image.Save(expected_filepath); + Assert.AreEqual(expected: expected, actual: actual, message: "fail EAN128.Generate({0})", parameters: builder.ToDisplayString()); + } + } + + [TestMethod] + public void EAN128_GenerateWithGS1BuilderAndBarcodeSettings() + { + var builder = new GS1Builder(); + var settings = new BarcodeSettings() + { + NarrowWidth = 4, + ModulePadding = 0, + }; + var data = new[] { + new { ai=98, Value="0007" + "0448" + "101110" + "200000" + "2" + "0000" }, + }; + data.ToList().ForEach(d => builder.Add(d.ai, d.Value)); + var target = new EAN128(); + var expected_filepath = string.Format("EAN128_{0}.bmp", string.Join( + string.Empty, data.Select(v => string.Format("{0:00}{1}", v.ai, v.Value)) + )); + using (var actual_image = target.Generate(value: builder, settings: settings)) + { + var expected = @"23e2df8b58d889fec31f39c2e19122d5b6e8e09fa61a14e2591b89239ae7e1"; + var actual = string.Join("", actual_image.ComputeHash().Select(_ => Convert.ToString(_, 16))); + actual_image.Save(expected_filepath); + Assert.AreEqual(expected: expected, actual: actual, message: "fail EAN128.Generate({0})", parameters: builder.ToDisplayString()); + } + } + } +} diff --git a/BarcodeWriterTests/GS1/GS1BuilderTest.cs b/BarcodeWriterTests/GS1/GS1BuilderTest.cs new file mode 100644 index 0000000..c0de164 --- /dev/null +++ b/BarcodeWriterTests/GS1/GS1BuilderTest.cs @@ -0,0 +1,136 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Barcodes; +using Barcodes.GS1; +using System.Linq; +using System.Text; + +namespace BarcodeWriterTests.GS1 +{ + [TestClass] + public class GS1BuilderTest + { + [TestMethod] + public void GS1Builder_Constructor() + { + var g = new GS1Builder(); + Assert.IsInstanceOfType(value: g, expectedType: typeof(GS1Builder), message: "fail constructor GS1Builder"); + Assert.AreEqual(Code128Helper.FNC1, g.FNC1, "fail default constructor"); + var g2 = new GS1Builder(Code128Helper.FNC1); + Assert.AreEqual(Code128Helper.FNC1, g2.FNC1, "fail constructor with FNC1 parameter"); + } + + [TestMethod] + public void GS1Builder_Add() + { + var g = new GS1Builder(); + var data = new[] + { + new { ai= 1, value="14912345678904" }, + new { ai= 420, value="1010001" }, + new { ai= 91, value="1234567890" }, + new { ai= 92, value="0" }, + }; + data.ToList() + .ForEach(v => g.Add(ai: v.ai, value: v.value)); + + CollectionAssert.AreEqual(expected: data.Select(v => v.ai).ToArray(), actual: g.AICollection, message: "fail add ai"); + CollectionAssert.AreEqual(expected: data.Select(v => v.value).ToArray(), actual: g.Values, message: "fail add value"); + } + + [TestMethod] + public void GS1Builder_RemoveAt() + { + var g = new GS1Builder(); + var ais = new int[] { 91, 92, 93 }; + var values = new string[] { "1234567890", "0", "93" }; + const int index = 1; + ais.Select((r, i) => new { ai = r, value = values[i] }).ToList() + .ForEach(r => g.Add(ai: r.ai, value: r.value)); + g.RemoveAt(index); + + Assert.IsTrue(g.AICollection.SequenceEqual(ais.Where((r, i) => i != index)), "fail add ai"); + Assert.IsTrue(g.Values.SequenceEqual(values.Where((r, i) => i != index)), "fail add values"); + } + + [TestMethod] + public void GS1Builder_ToStringWithoutParameter() + { + var g = new GS1Builder(); + var data = new[] + { + new { ai= 1, value="14912345678904", Terminate=String.Empty }, + new { ai= 420, value="1010001", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 91, value="1234567890", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 92, value="0", Terminate=Code128Helper.FNC1.ToString() }, + }; + data.ToList() + .ForEach(v => g.Add(ai: v.ai, value: v.value)); + var sb = new StringBuilder(); + sb.Append(Code128Helper.FNC1); + data.ToList() + .ForEach(v => + { + sb.AppendFormat("{0:00}", v.ai); + sb.Append(v.value); + sb.Append(v.Terminate); + }); + var expected = Encoding.ASCII.GetBytes(sb.ToString()); + var actual = Encoding.ASCII.GetBytes(g.ToString()); + CollectionAssert.AreEqual(expected: expected, actual: actual, message: "fail ToString()"); + } + + [TestMethod] + public void GS1Builder_ToStringWithParameter() + { + var g = new GS1Builder(); + var data = new[] + { + new { ai= 1, value="14912345678904", Terminate=String.Empty }, + new { ai= 420, value="1010001", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 91, value="1234567890", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 92, value="0", Terminate=Code128Helper.FNC1.ToString() }, + }; + data.ToList() + .ForEach(v => g.Add(ai: v.ai, value: v.value)); + var sb = new StringBuilder(); + sb.Append(Code128Helper.FNC1); + data.ToList() + .ForEach(v => + { + sb.AppendFormat("{0:00}", v.ai); + sb.Append(v.value); + sb.Append(v.Terminate); + }); + var expected = Encoding.ASCII.GetBytes(sb.ToString()); + var actual = Encoding.ASCII.GetBytes(g.ToString(Code128Helper.FNC2)); + CollectionAssert.AreEqual(expected: expected, actual: actual, message: "fail ToString(fnc1)"); + Assert.AreEqual(expected: Code128Helper.FNC2, actual: g.FNC1, message: "fail FNC1"); + } + + [TestMethod] + public void GS1Builder_ToDisplayString() + { + var g = new GS1Builder(); + var data = new[] + { + new { ai= 1, value="14912345678904", Terminate=String.Empty }, + new { ai= 420, value="1010001", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 91, value="1234567890", Terminate=Code128Helper.FNC1.ToString() }, + new { ai= 92, value="0", Terminate=Code128Helper.FNC1.ToString() }, + }; + data.ToList() + .ForEach(v => g.Add(ai: v.ai, value: v.value)); + var sb = new StringBuilder(); + data.ToList() + .ForEach(v => + { + sb.AppendFormat("({0:00})", v.ai); + sb.Append(v.value); + }); + var expected = sb.ToString(); + var actual = g.ToDisplayString(); + Assert.AreEqual(expected: expected, actual: actual, message: "fail ToDisplayString()"); + } + } +} diff --git a/BarcodeWriterTests/Helpers/ImageHelper.cs b/BarcodeWriterTests/Helpers/ImageHelper.cs new file mode 100644 index 0000000..40b4c88 --- /dev/null +++ b/BarcodeWriterTests/Helpers/ImageHelper.cs @@ -0,0 +1,78 @@ +using System.Drawing; +using System.Drawing.Imaging; +using System.Linq; +using System.Runtime.InteropServices; + +namespace BarcodeWriterTests.Helpers +{ + public static class ImageHelper + { + public static bool AreEquals(this Image self, Image target) + { + var img1 = self as Bitmap; + var img2 = target as Bitmap; + + //高さが違えばfalse + if (img1.Width != img2.Width || img1.Height != img2.Height) return false; + //BitmapData取得 + var bd1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), ImageLockMode.ReadOnly, img1.PixelFormat); + var bd2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), ImageLockMode.ReadOnly, img2.PixelFormat); + try + { + //スキャン幅が違う場合はfalse + if (bd1.Stride != bd2.Stride) + return false; + + int bsize = bd1.Stride * img1.Height; + var byte1 = new byte[bsize]; + var byte2 = new byte[bsize]; + //バイト配列にコピー + Marshal.Copy(bd1.Scan0, byte1, 0, bsize); + Marshal.Copy(bd2.Scan0, byte2, 0, bsize); + + //MD5ハッシュを取る + var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); + var hash1 = md5.ComputeHash(byte1); + var hash2 = md5.ComputeHash(byte2); + + //ハッシュを比較 + return hash1.SequenceEqual(hash2); + } + finally + { + //ロックを解除 + img1.UnlockBits(bd1); + img2.UnlockBits(bd2); + } + } + + + public static byte[] ComputeHash(this Image self) + { + var img1 = self as Bitmap; + + //BitmapData取得 + var bd1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), ImageLockMode.ReadOnly, img1.PixelFormat); + try + { + + int bsize = bd1.Stride * img1.Height; + var byte1 = new byte[bsize]; + + //バイト配列にコピー + Marshal.Copy(bd1.Scan0, byte1, 0, bsize); + + //MD5ハッシュを取る + //var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); + using (var sum = new System.Security.Cryptography.SHA256CryptoServiceProvider()) + return sum.ComputeHash(byte1); + + } + finally + { + //ロックを解除 + img1.UnlockBits(bd1); + } + } + } +} diff --git a/BarcodeWriterTests/Properties/AssemblyInfo.cs b/BarcodeWriterTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6c4d066 --- /dev/null +++ b/BarcodeWriterTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 +// アセンブリに関連付けられている情報を変更するには、 +// これらの属性値を変更してください。 +[assembly: AssemblyTitle("BarcodeWriterTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BarcodeWriterTests")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから +// 参照できなくなります。このアセンブリ内で COM から型にアクセスする必要がある場合は、 +// その型の ComVisible 属性を true に設定してください。 +[assembly: ComVisible(false)] + +// このプロジェクトが COM に公開される場合、次の GUID が typelib の ID になります +[assembly: Guid("d694dbf2-f65c-4c92-9acc-629a40214d7d")] + +// アセンブリのバージョン情報は次の 4 つの値で構成されています: +// +// メジャー バージョン +// マイナー バージョン +// ビルド番号 +// Revision +// +// すべての値を指定するか、以下のように '*' を使用してビルド番号とリビジョン番号を +// 既定値にすることができます: +//[アセンブリ: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]