From f949ad6b4090c9b72c612597fda705991094ca12 Mon Sep 17 00:00:00 2001 From: primo-ppcg Date: Fri, 26 Apr 2024 10:04:29 +0700 Subject: [PATCH] Drop BarcodeLib dependency (#229) * Drop BarcodeLib dependency * don't inline regex --- .../Labels/Example/Example12-102x152.zpl2 | 2 +- .../Labels/Test/Barcode128-102x152.zpl2 | 12 +- .../Labels/Test/BarcodeEAN13-102x152.zpl2 | 22 +- .../BinaryKits.Zpl.Viewer.csproj | 2 - .../ElementDrawers/Barcode128ElementDrawer.cs | 117 ++---- .../ElementDrawers/Barcode39ElementDrawer.cs | 48 +-- .../ElementDrawers/Barcode93ElementDrawer.cs | 33 +- .../ElementDrawers/BarcodeDrawerBase.cs | 187 ++++++--- .../BarcodeEAN13ElementDrawer.cs | 119 ++++-- .../ElementDrawers/DataMatrixElementDrawer.cs | 26 +- .../ElementDrawers/DrawerOptions.cs | 6 +- .../ElementDrawers/ElementDrawerBase.cs | 5 +- .../ElementDrawers/FieldBlockElementDrawer.cs | 24 +- .../ElementDrawers/GraphicBoxElementDrawer.cs | 15 +- .../GraphicCircleElementDrawer.cs | 13 +- .../GraphicFieldElementDrawer.cs | 4 +- .../ElementDrawers/IElementDrawer.cs | 7 +- .../ElementDrawers/ImageMoveElementDrawer.cs | 3 + .../Interleaved2of5BarcodeDrawer.cs | 43 +- .../ElementDrawers/MaxiCodeElementDrawer.cs | 5 +- .../ElementDrawers/PDF417ElementDrawer.cs | 2 +- .../ElementDrawers/QrCodeElementDrawer.cs | 15 +- .../RecallGraphicElementDrawer.cs | 3 + .../ElementDrawers/TextFieldElementDrawer.cs | 24 +- .../Helpers/FontHelper.cs | 28 -- .../Symologies/ZplCode128Symbology.cs | 391 ++++++++++++++++++ src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs | 4 +- src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs | 2 +- 28 files changed, 817 insertions(+), 345 deletions(-) delete mode 100644 src/BinaryKits.Zpl.Viewer/Helpers/FontHelper.cs create mode 100644 src/BinaryKits.Zpl.Viewer/Symologies/ZplCode128Symbology.cs diff --git a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Example/Example12-102x152.zpl2 b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Example/Example12-102x152.zpl2 index f5de016a..4e86bda9 100644 --- a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Example/Example12-102x152.zpl2 +++ b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Example/Example12-102x152.zpl2 @@ -107,4 +107,4 @@ FFFFFFFFFFFFFFFFFFFFFFFFFFFFF00C1B6C00 ^FT105,982^BY3^BCN,202,N,N,,A^FV1ZRW01750399905329^FS ^FT273,896^A0N,95,74^FVSAMPLE^FS -^XZ^XZ +^XZ diff --git a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/Barcode128-102x152.zpl2 b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/Barcode128-102x152.zpl2 index dca1ed02..e32c9662 100644 --- a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/Barcode128-102x152.zpl2 +++ b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/Barcode128-102x152.zpl2 @@ -5,33 +5,33 @@ ^FO10,10 ^BY3 ^BCN,100,Y -^FD123ABC^FS +^FDABC12345^FS ^FO10,160 ^BY4 ^BCN,100,Y -^FD123ABC^FS +^FDABC12345^FS ^FO10,320 ^BY5 ^BCN,100,Y -^FD123ABC^FS +^FDABC12345^FS ^FX Automatic Mode ^FO10,500 ^BY3 ^BCN,100,Y,,,A -^FD123ABC^FS +^FDABC12345^FS ^FO10,650 ^BY4 ^BCN,100,Y,,,A -^FD123ABC^FS +^FDABC12345^FS ^FO10,810 ^BY5 ^BCN,100,Y,,,A -^FD123ABC^FS +^FDABC12345^FS ^XZ \ No newline at end of file diff --git a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/BarcodeEAN13-102x152.zpl2 b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/BarcodeEAN13-102x152.zpl2 index e63e695a..8e8b7423 100644 --- a/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/BarcodeEAN13-102x152.zpl2 +++ b/src/BinaryKits.Zpl.Viewer.WebApi/Labels/Test/BarcodeEAN13-102x152.zpl2 @@ -1,18 +1,28 @@ ^XA -^FO10,10 -^BY3 +^FO60,10 +^BY1 +^BEN,50,Y +^FD123456789012^FS + +^FO60,90 +^BY2 ^BEN,100,Y ^FD123456789012^FS -^FO10,160 +^FO60,230 +^BY3 +^BEN,150,Y +^FD123456789012^FS + +^FO60,430 ^BY4 -^BEN,100,Y +^BEN,200,Y ^FD123456789012^FS -^FO10,320 +^FO60,700 ^BY5 -^BEN,100,Y +^BEN,250,Y ^FD123456789012^FS ^XZ \ No newline at end of file diff --git a/src/BinaryKits.Zpl.Viewer/BinaryKits.Zpl.Viewer.csproj b/src/BinaryKits.Zpl.Viewer/BinaryKits.Zpl.Viewer.csproj index 2075cfbb..a2704fc2 100644 --- a/src/BinaryKits.Zpl.Viewer/BinaryKits.Zpl.Viewer.csproj +++ b/src/BinaryKits.Zpl.Viewer/BinaryKits.Zpl.Viewer.csproj @@ -22,8 +22,6 @@ - - diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode128ElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode128ElementDrawer.cs index f72a2a2f..79f3b330 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode128ElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode128ElementDrawer.cs @@ -1,138 +1,73 @@ -using BarcodeLib; using BinaryKits.Zpl.Label.Elements; -using BinaryKits.Zpl.Viewer.Helpers; +using BinaryKits.Zpl.Viewer.Symologies; using SkiaSharp; using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text.RegularExpressions; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Code 128 Barcode elements + /// public class Barcode128ElementDrawer : BarcodeDrawerBase { - /// - /// Start sequence lookups. - /// - /// - private static readonly Dictionary startCodeMap = new Dictionary() - { - { ">6", TYPE.CODE128A }, - { ">9", TYPE.CODE128A }, - { ">:", TYPE.CODE128B }, - { ">;", TYPE.CODE128C }, - { ">5", TYPE.CODE128C }, - }; - - private static readonly Regex startCodeRegex = new Regex(@"(>[569:;])(.+)", RegexOptions.Compiled); - private static readonly Regex invalidInvocationRegex = new Regex(@"(?[569:;]", RegexOptions.Compiled); - - // As defined in BarcodeLib.Symbologies.Code128 - private static readonly string FNC1 = Convert.ToChar(200).ToString(); - /// public override bool CanDraw(ZplElementBase element) { return element is ZplBarcode128; } - /// - public override void Draw(ZplElementBase element) - { - Draw(element, new DrawerOptions()); - } - /// public override void Draw(ZplElementBase element, DrawerOptions options) { if (element is ZplBarcode128 barcode) { - var barcodeType = TYPE.CODE128; - - //remove the start code form the content we only support the globals N,A,D,U and our barcode library doesn't support these types - string content = startCodeRegex.Replace(barcode.Content, ""); - string interpretation = content; - - // remove any start sequences not at the start of the content (invalid invocation) - content = invalidInvocationRegex.Replace(content, ""); - interpretation = content; - - // support hand-rolled GS1 - content = content.Replace(">8", FNC1); - interpretation = interpretation.Replace(">8", ""); - + string content = barcode.Content; + Code128CodeSet codeSet = Code128CodeSet.Code128B; + bool gs1 = false; if (string.IsNullOrEmpty(barcode.Mode) || barcode.Mode == "N") { - Match startCodeMatch = startCodeRegex.Match(barcode.Content); - if (startCodeMatch.Success) - { - barcodeType = TYPE.CODE128; - //TODO: Instead of using the auto type, switch type for each part of the content - //>:+B210AC>50270>6/$+2>5023080000582>6L - //[TYPE.CODE128B]+B210AC - //[TYPE.CODE128C]0270 - //[TYPE.CODE128A]+/$+2 - //[TYPE.CODE128C]023080000582 - //[TYPE.CODE128A]L - } - - // support hand-rolled GS1 - content = content.Replace(">8", FNC1); - interpretation = interpretation.Replace(">8", ""); - // TODO: support remaining escapes within a barcode + codeSet = Code128CodeSet.Code128B; } else if (barcode.Mode == "A") { - //A (automatic mode, the ZPL engine automatically determines the subsets that are used to encode the data) - barcodeType = TYPE.CODE128; // dynamic + codeSet = Code128CodeSet.Code128; } else if (barcode.Mode == "D") { - //D (UCC/EAN mode, field data must contain GS1 numbers) - barcodeType = TYPE.CODE128C; - - if (!content.StartsWith(FNC1)) - { - content = FNC1 + content; - } + codeSet = Code128CodeSet.Code128C; + gs1 = true; } else if (barcode.Mode == "U") { - //U (UCC case mode, field data must contain 19 digits) - barcodeType = TYPE.CODE128C; + codeSet = Code128CodeSet.Code128C; + gs1 = true; content = content.PadLeft(19, '0').Substring(0, 19); int checksum = 0; for (int i = 0; i < 19; i++) { checksum += (content[i] - 48) * (i % 2 * 2 + 7); } - interpretation = string.Format("{0}{1}", interpretation, checksum % 10); - content = string.Format("{0}{1}{2}", FNC1, content, checksum % 10); + content = $">8{content}{checksum % 10}"; } float x = barcode.PositionX; float y = barcode.PositionY; - float labelFontSize = Math.Min(barcode.ModuleWidth * 7.2f, 72f); - var labelTypeFace = options.FontLoader("A"); - var labelFont = new SKFont(labelTypeFace, labelFontSize).ToSystemDrawingFont(); - int labelHeight = barcode.PrintInterpretationLine ? labelFont.Height : 0; - int labelHeightOffset = barcode.PrintInterpretationLineAboveCode ? labelHeight : 0; + var (data, interpretation) = ZplCode128Symbology.Encode(content, codeSet, gs1); + using var resizedImage = this.BoolArrayToSKBitmap(data.ToArray(), barcode.Height, barcode.ModuleWidth); + var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation); - var barcodeElement = new Barcode + if (barcode.PrintInterpretationLine) { - BarWidth = barcode.ModuleWidth, - BackColor = Color.Transparent, - Height = barcode.Height + labelHeight, - IncludeLabel = barcode.PrintInterpretationLine, - LabelPosition = barcode.PrintInterpretationLineAboveCode ? LabelPositions.TOPCENTER : LabelPositions.BOTTOMCENTER, - LabelFont = labelFont, - AlternateLabel = interpretation - }; - - using var image = barcodeElement.Encode(barcodeType, content); - this.DrawBarcode(this.GetImageData(image), barcode.Height, image.Width, barcode.FieldOrigin != null, x, y, labelHeightOffset, barcode.FieldOrientation); + // TODO: use font 0, auto scale for Mode D + float labelFontSize = Math.Min(barcode.ModuleWidth * 10f, 100f); + var labelTypeFace = options.FontLoader("A"); + var labelFont = new SKFont(labelTypeFace, labelFontSize); + this.DrawInterpretationLine(interpretation, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation, barcode.PrintInterpretationLineAboveCode, options); + } } } + } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode39ElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode39ElementDrawer.cs index 7936ee16..dab0a022 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode39ElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode39ElementDrawer.cs @@ -1,12 +1,13 @@ -using BarcodeLib; using BinaryKits.Zpl.Label.Elements; -using BinaryKits.Zpl.Viewer.Helpers; using SkiaSharp; using System; -using System.Drawing; +using ZXing.OneD; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Code 39 Barcode elements + /// public class Barcode39ElementDrawer : BarcodeDrawerBase { /// @@ -15,12 +16,6 @@ public override bool CanDraw(ZplElementBase element) return element is ZplBarcode39; } - /// - public override void Draw(ZplElementBase element) - { - Draw(element, new DrawerOptions()); - } - /// public override void Draw(ZplElementBase element, DrawerOptions options) { @@ -29,28 +24,25 @@ public override void Draw(ZplElementBase element, DrawerOptions options) float x = barcode.PositionX; float y = barcode.PositionY; - var content = barcode.Content; - var interpretation = string.Format("*{0}*", content.Trim('*')); + var content = barcode.Content.Trim('*'); + var interpretation = string.Format("*{0}*", content); - float labelFontSize = Math.Min(barcode.ModuleWidth * 7.2f, 72f); - var labelTypeFace = options.FontLoader("A"); - var labelFont = new SKFont(labelTypeFace, labelFontSize).ToSystemDrawingFont(); - int labelHeight = barcode.PrintInterpretationLine ? labelFont.Height : 0; - int labelHeightOffset = barcode.PrintInterpretationLineAboveCode ? labelHeight : 0; + var writer = new Code39Writer(); + var result = writer.encode(content); + int narrow = barcode.ModuleWidth; + int wide = (int)Math.Floor(barcode.WideBarToNarrowBarWidthRatio * narrow); + result = this.AdjustWidths(result, wide, narrow); + using var resizedImage = this.BoolArrayToSKBitmap(result, barcode.Height); + var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation); - var barcodeElement = new Barcode + if (barcode.PrintInterpretationLine) { - BarWidth = barcode.ModuleWidth, - BackColor = Color.Transparent, - Height = barcode.Height + labelHeight, - IncludeLabel = barcode.PrintInterpretationLine, - LabelPosition = barcode.PrintInterpretationLineAboveCode ? LabelPositions.TOPCENTER : LabelPositions.BOTTOMCENTER, - LabelFont = labelFont, - AlternateLabel = interpretation - }; - - using var image = barcodeElement.Encode(TYPE.CODE39Extended, content); - this.DrawBarcode(this.GetImageData(image), barcode.Height, image.Width, barcode.FieldOrigin != null, x, y, labelHeightOffset, barcode.FieldOrientation); + float labelFontSize = Math.Min(barcode.ModuleWidth * 10f, 100f); + var labelTypeFace = options.FontLoader("A"); + var labelFont = new SKFont(labelTypeFace, labelFontSize); + this.DrawInterpretationLine(interpretation, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation, barcode.PrintInterpretationLineAboveCode, options); + } } } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode93ElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode93ElementDrawer.cs index 3cd44bd9..3f1a032c 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode93ElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Barcode93ElementDrawer.cs @@ -1,9 +1,7 @@ -using BarcodeLib; -using BinaryKits.Zpl.Label.Elements; -using BinaryKits.Zpl.Viewer.Helpers; +using BinaryKits.Zpl.Label.Elements; using SkiaSharp; using System; -using System.Drawing; +using ZXing.OneD; namespace BinaryKits.Zpl.Viewer.ElementDrawers { @@ -31,25 +29,20 @@ public override void Draw(ZplElementBase element, DrawerOptions options) var content = barcode.Content; - float labelFontSize = Math.Min(barcode.ModuleWidth * 7.2f, 72f); - var labelTypeFace = options.FontLoader("A"); - var labelFont = new SKFont(labelTypeFace, labelFontSize).ToSystemDrawingFont(); - int labelHeight = barcode.PrintInterpretationLine ? labelFont.Height : 0; - int labelHeightOffset = barcode.PrintInterpretationLineAboveCode ? labelHeight : 0; + var writer = new Code93Writer(); + var result = writer.encode(content); + using var resizedImage = this.BoolArrayToSKBitmap(result, barcode.Height, barcode.ModuleWidth); + var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation); - var barcodeElement = new Barcode + if (barcode.PrintInterpretationLine) { - BarWidth = barcode.ModuleWidth, - BackColor = Color.Transparent, - Height = barcode.Height + labelHeight, - IncludeLabel = barcode.PrintInterpretationLine, - LabelPosition = barcode.PrintInterpretationLineAboveCode ? LabelPositions.TOPCENTER : LabelPositions.BOTTOMCENTER, - LabelFont = labelFont, - AlternateLabel = content - }; + float labelFontSize = Math.Min(barcode.ModuleWidth * 10f, 100f); + var labelTypeFace = options.FontLoader("A"); + var labelFont = new SKFont(labelTypeFace, labelFontSize); + this.DrawInterpretationLine(content, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrientation != null, barcode.FieldOrientation, barcode.PrintInterpretationLineAboveCode, options); + } - using var image = barcodeElement.Encode(TYPE.CODE93, content); - this.DrawBarcode(this.GetImageData(image), barcode.Height, image.Width, barcode.FieldOrigin != null, x, y, labelHeightOffset, barcode.FieldOrientation); } } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeDrawerBase.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeDrawerBase.cs index 25ddbcf2..eead6e13 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeDrawerBase.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeDrawerBase.cs @@ -1,81 +1,148 @@ using SkiaSharp; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; +using SkiaSharp.HarfBuzz; +using System; +using System.Collections.Generic; +using System.Linq; using ZXing.Common; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Base clase for Barcode element drawers + /// public abstract class BarcodeDrawerBase : ElementDrawerBase { - public byte[] GetImageData(Image image) + /// + /// Minimum acceptable magin between a barcode and its interpretation line, in pixels + /// + protected const float MIN_LABEL_MARGIN = 5f; + + protected void DrawBarcode( + byte[] barcodeImageData, + float x, + float y, + int barcodeWidth, + int barcodeHeight, + bool useFieldOrigin, + Label.FieldOrientation fieldOrientation) { - using (var memoryStream = new MemoryStream()) + using (new SKAutoCanvasRestore(this._skCanvas)) { - image.Save(memoryStream, ImageFormat.Png); - return memoryStream.ToArray(); + SKMatrix matrix = this.GetRotationMatrix(x, y, barcodeWidth, barcodeHeight, useFieldOrigin, fieldOrientation); + + if (!useFieldOrigin) + { + y -= barcodeHeight; + } + + if (matrix != SKMatrix.Empty) + { + this._skCanvas.SetMatrix(matrix); + } + + this._skCanvas.DrawBitmap(SKBitmap.Decode(barcodeImageData), x, y); } } - public void DrawBarcode( - byte[] barcodeImageData, - int barcodeHeight, - int barcodeWidth, - bool useFieldOrigin, + protected void DrawInterpretationLine( + string interpretation, + SKFont skFont, float x, float y, - int labelHeightOffset, - Label.FieldOrientation fieldOrientation) + int barcodeWidth, + int barcodeHeight, + bool useFieldOrigin, + Label.FieldOrientation fieldOrientation, + bool printInterpretationLineAboveCode, + DrawerOptions options) { using (new SKAutoCanvasRestore(this._skCanvas)) { - SKMatrix matrix = SKMatrix.Empty; + using var skPaint = new SKPaint(skFont); + skPaint.IsAntialias = options.Antialias; + + SKMatrix matrix = this.GetRotationMatrix(x, y, barcodeWidth, barcodeHeight, useFieldOrigin, fieldOrientation); - if (useFieldOrigin) + if (matrix != SKMatrix.Empty) { - switch (fieldOrientation) - { - case Label.FieldOrientation.Rotated90: - matrix = SKMatrix.CreateRotationDegrees(90, x + barcodeHeight / 2, y + barcodeHeight / 2); - break; - case Label.FieldOrientation.Rotated180: - matrix = SKMatrix.CreateRotationDegrees(180, x + barcodeWidth / 2, y + barcodeHeight / 2); - break; - case Label.FieldOrientation.Rotated270: - matrix = SKMatrix.CreateRotationDegrees(270, x + barcodeWidth / 2, y + barcodeWidth / 2); - break; - case Label.FieldOrientation.Normal: - break; - } + this._skCanvas.SetMatrix(matrix); } - else + + var textBounds = new SKRect(); + skPaint.MeasureText(interpretation, ref textBounds); + + x += (barcodeWidth - textBounds.Width) / 2; + if (!useFieldOrigin) { - switch (fieldOrientation) - { - case Label.FieldOrientation.Rotated90: - matrix = SKMatrix.CreateRotationDegrees(90, x, y); - break; - case Label.FieldOrientation.Rotated180: - matrix = SKMatrix.CreateRotationDegrees(180, x, y); - break; - case Label.FieldOrientation.Rotated270: - matrix = SKMatrix.CreateRotationDegrees(270, x, y); - break; - case Label.FieldOrientation.Normal: - break; - } y -= barcodeHeight; } - y -= labelHeightOffset; + float margin = Math.Max((skFont.Spacing - textBounds.Height) / 2, MIN_LABEL_MARGIN); - if (matrix != SKMatrix.Empty) + if (printInterpretationLineAboveCode) { - this._skCanvas.SetMatrix(matrix); + this._skCanvas.DrawShapedText(interpretation, x, y - margin, skPaint); } + else + { + this._skCanvas.DrawShapedText(interpretation, x, y + barcodeHeight + textBounds.Height + margin, skPaint); + } + } + } - this._skCanvas.DrawBitmap(SKBitmap.Decode(barcodeImageData), x, y); + protected SKMatrix GetRotationMatrix(float x, float y, int width, int height, bool useFieldOrigin, Label.FieldOrientation fieldOrientation) + { + SKMatrix matrix = SKMatrix.Empty; + + if (useFieldOrigin) + { + switch (fieldOrientation) + { + case Label.FieldOrientation.Rotated90: + matrix = SKMatrix.CreateRotationDegrees(90, x + height / 2, y + height / 2); + break; + case Label.FieldOrientation.Rotated180: + matrix = SKMatrix.CreateRotationDegrees(180, x + width / 2, y + height / 2); + break; + case Label.FieldOrientation.Rotated270: + matrix = SKMatrix.CreateRotationDegrees(270, x + width / 2, y + width / 2); + break; + case Label.FieldOrientation.Normal: + break; + } + } + else + { + switch (fieldOrientation) + { + case Label.FieldOrientation.Rotated90: + matrix = SKMatrix.CreateRotationDegrees(90, x, y); + break; + case Label.FieldOrientation.Rotated180: + matrix = SKMatrix.CreateRotationDegrees(180, x, y); + break; + case Label.FieldOrientation.Rotated270: + matrix = SKMatrix.CreateRotationDegrees(270, x, y); + break; + case Label.FieldOrientation.Normal: + break; + } + } + + return matrix; + } + + protected SKBitmap BoolArrayToSKBitmap(bool[] array, int height, int moduleWidth = 1) + { + using var image = new SKBitmap(array.Length, 1); + + for (int col = 0; col < array.Length; col++) + { + var color = array[col] ? SKColors.Black : SKColors.Transparent; + image.SetPixel(col, 0, color); } + + return image.Resize(new SKSizeI(image.Width * moduleWidth, height), SKFilterQuality.None); } protected SKBitmap BitMatrixToSKBitmap(BitMatrix matrix, int pixelScale) @@ -93,5 +160,27 @@ protected SKBitmap BitMatrixToSKBitmap(BitMatrix matrix, int pixelScale) return image.Resize(new SKSizeI(image.Width * pixelScale, image.Height * pixelScale), SKFilterQuality.None); } + + protected bool[] AdjustWidths(bool[] array, int wide, int narrow) + { + List result = new List(); + var last = true; + var count = 0; + foreach (var current in array) + { + if (current != last) + { + result.AddRange(Enumerable.Repeat(last, count == 1 ? narrow : wide)); + last = current; + count = 0; + } + + count += 1; + } + + result.AddRange(Enumerable.Repeat(last, narrow)); + + return result.ToArray(); + } } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeEAN13ElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeEAN13ElementDrawer.cs index 5a3e5031..c0ca0b1f 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeEAN13ElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/BarcodeEAN13ElementDrawer.cs @@ -1,23 +1,29 @@ -using BarcodeLib; using BinaryKits.Zpl.Label.Elements; -using BinaryKits.Zpl.Viewer.Helpers; using SkiaSharp; using System; -using System.Drawing; +using ZXing.OneD; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for EAN-13 Barcode elements + /// public class BarcodeEAN13ElementDrawer : BarcodeDrawerBase { - public override bool CanDraw(ZplElementBase element) + private static readonly bool[] guards = new bool[95]; + + static BarcodeEAN13ElementDrawer() { - return element is ZplBarcodeEan13; + foreach (int idx in new int[] { 0, 2, 46, 48, 92, 94 }) + { + guards[idx] = true; + } } /// - public override void Draw(ZplElementBase element) + public override bool CanDraw(ZplElementBase element) { - Draw(element, new DrawerOptions()); + return element is ZplBarcodeEan13; } /// @@ -32,37 +38,90 @@ public override void Draw(ZplElementBase element, DrawerOptions options) content = content.PadLeft(12, '0').Substring(0, 12); var interpretation = content; - if (barcode.PrintInterpretationLineAboveCode) + int checksum = 0; + for (int i = 0; i < 12; i++) + { + checksum += (content[i] - 48) * (9 - i % 2 * 2); + } + interpretation = string.Format("{0}{1}", interpretation, checksum % 10); + + var writer = new EAN13Writer(); + var result = writer.encode(content); + using var resizedImage = this.BoolArrayToSKBitmap(result, barcode.Height, barcode.ModuleWidth); + var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation); + + if (barcode.PrintInterpretationLine) { - int checksum = 0; - for (int i = 0; i < 12; i++) + float labelFontSize = Math.Min(barcode.ModuleWidth * 10f, 100f); + var labelTypeFace = options.FontLoader("A"); + var labelFont = new SKFont(labelTypeFace, labelFontSize); + if (barcode.PrintInterpretationLineAboveCode) + { + this.DrawInterpretationLine(interpretation, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation, true, options); + } + else { - checksum += (content[i] - 48) * (9 - i % 2 * 2); + this.DrawEAN13InterpretationLine(interpretation, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation, barcode.ModuleWidth, options); } - interpretation = string.Format("{0}{1}", interpretation, checksum % 10); } - float labelFontSize = Math.Min(barcode.ModuleWidth * 7.2f, 72f); - var labelTypeFace = options.FontLoader("A"); - var labelFont = new SKFont(labelTypeFace, labelFontSize).ToSystemDrawingFont(); - int labelHeight = barcode.PrintInterpretationLine ? labelFont.Height : 0; - int labelHeightOffset = barcode.PrintInterpretationLineAboveCode ? labelHeight : 0; + } + } + + private void DrawEAN13InterpretationLine( + string interpretation, + SKFont skFont, + float x, + float y, + int barcodeWidth, + int barcodeHeight, + bool useFieldOrigin, + Label.FieldOrientation fieldOrientation, + int moduleWidth, + DrawerOptions options) + { + using (new SKAutoCanvasRestore(this._skCanvas)) + { + using var skPaint = new SKPaint(skFont); + skPaint.IsAntialias = options.Antialias; + + SKMatrix matrix = this.GetRotationMatrix(x, y, barcodeWidth, barcodeHeight, useFieldOrigin, fieldOrientation); + + if (matrix != SKMatrix.Empty) + { + this._skCanvas.SetMatrix(matrix); + } + + var textBounds = new SKRect(); + skPaint.MeasureText(interpretation, ref textBounds); + + if (!useFieldOrigin) + { + y -= barcodeHeight; + } + + float margin = Math.Max((skFont.Spacing - textBounds.Height) / 2, MIN_LABEL_MARGIN); + int spacing = moduleWidth * 7; + + using var guardImage = this.BoolArrayToSKBitmap(guards, (int)(margin + textBounds.Height / 2), moduleWidth); + var guardPng = guardImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this._skCanvas.DrawBitmap(SKBitmap.Decode(guardPng), x, y + barcodeHeight); - var barcodeElement = new Barcode + for (int i = 0; i < interpretation.Length; i++) { - BarWidth = barcode.ModuleWidth, - BackColor = Color.White, - Height = barcode.Height + labelHeight, - IncludeLabel = barcode.PrintInterpretationLine, - LabelPosition = barcode.PrintInterpretationLineAboveCode ? LabelPositions.TOPCENTER : LabelPositions.BOTTOMCENTER, - LabelFont = labelFont, - AlternateLabel = interpretation, - StandardizeLabel = !barcode.PrintInterpretationLineAboveCode - }; - - using var image = barcodeElement.Encode(TYPE.EAN13, content); - this.DrawBarcode(this.GetImageData(image), barcode.Height, image.Width, barcode.FieldOrigin != null, x, y, labelHeightOffset, barcode.FieldOrientation); + string digit = interpretation[i].ToString(); + var digitBounds = new SKRect(); + skPaint.MeasureText(digit, ref digitBounds); + this._skCanvas.DrawText(digit, x - (spacing + digitBounds.Width) / 2 - moduleWidth, y + barcodeHeight + textBounds.Height + margin, skPaint); + x += spacing; + if (i == 0 || i == 6) + { + x += moduleWidth * 4; + } + } } } + } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/DataMatrixElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/DataMatrixElementDrawer.cs index adc92990..fc6f70a5 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/DataMatrixElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/DataMatrixElementDrawer.cs @@ -1,6 +1,5 @@ using BinaryKits.Zpl.Label.Elements; using SkiaSharp; -using System.Collections.Generic; using System.Text.RegularExpressions; using ZXing; using ZXing.Datamatrix; @@ -8,8 +7,13 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Data Matrix Barcode elements + /// public class DataMatrixElementDrawer : BarcodeDrawerBase { + private static readonly Regex gs1Regex = new Regex(@"^_1(.+)$", RegexOptions.Compiled); + /// public override bool CanDraw(ZplElementBase element) { @@ -33,26 +37,30 @@ public override void Draw(ZplElementBase element) // support hand-rolled GS1 bool gs1Mode = false; var content = dataMatrix.Content; - if (Regex.Match(content, @"(^_1)", RegexOptions.None).Success) + + Match gs1Match = gs1Regex.Match(content); + if (gs1Match.Success) { - content = Regex.Replace(content, @"(^_1)", ""); + content = gs1Match.Groups[1].Value; gs1Mode = true; } var writer = new DataMatrixWriter(); - var hints = new Dictionary { - { EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE }, - { EncodeHintType.DATA_MATRIX_COMPACT, gs1Mode }, - { EncodeHintType.GS1_FORMAT, gs1Mode } + var encodingOptions = new DatamatrixEncodingOptions() + { + SymbolShape = SymbolShapeHint.FORCE_SQUARE, + CompactEncoding = gs1Mode, + GS1Format = gs1Mode }; - var result = writer.encode(content, BarcodeFormat.DATA_MATRIX, 0, 0, hints); + var result = writer.encode(content, BarcodeFormat.DATA_MATRIX, 0, 0, encodingOptions.Hints); using var resizedImage = this.BitMatrixToSKBitmap(result, dataMatrix.Height); { var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); - this.DrawBarcode(png, resizedImage.Height, resizedImage.Width, dataMatrix.FieldOrigin != null, x, y, 0, dataMatrix.FieldOrientation); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, dataMatrix.FieldOrigin != null, dataMatrix.FieldOrientation); } } } + } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/DrawerOptions.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/DrawerOptions.cs index f51ef71f..ad2f5245 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/DrawerOptions.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/DrawerOptions.cs @@ -11,6 +11,8 @@ public class DrawerOptions public SKEncodedImageFormat RenderFormat { get; set; } = SKEncodedImageFormat.Png; + public int RenderQuality { get; set; } = 80; + /// /// Applies label over a white background after rendering all elements /// @@ -21,10 +23,10 @@ public class DrawerOptions /// public bool PdfOutput { get; set; } = false; - public int RenderQuality { get; set; } = 80; - public bool ReplaceDashWithEnDash { get; set; } = true; + public bool Antialias { get; set; } = true; + public static Func DefaultFontLoader = fontName => { var typeface = SKTypeface.Default; var skFontManager = SKFontManager.Default; diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/ElementDrawerBase.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/ElementDrawerBase.cs index 8dc68fa7..3947c232 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/ElementDrawerBase.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/ElementDrawerBase.cs @@ -39,7 +39,10 @@ public virtual bool ForceBitmapDraw(ZplElementBase element) } /// - public abstract void Draw(ZplElementBase element); + public virtual void Draw(ZplElementBase element) + { + Draw(element, new DrawerOptions()); + } /// public virtual void Draw(ZplElementBase element, DrawerOptions options = null) diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/FieldBlockElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/FieldBlockElementDrawer.cs index a0513190..5e59cdb9 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/FieldBlockElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/FieldBlockElementDrawer.cs @@ -1,8 +1,10 @@ using BinaryKits.Zpl.Label; using BinaryKits.Zpl.Label.Elements; using BinaryKits.Zpl.Viewer.Helpers; + using SkiaSharp; using SkiaSharp.HarfBuzz; + using System; using System.Collections.Generic; using System.Linq; @@ -10,6 +12,9 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Field Block elements + /// public class FieldBlockElementDrawer : ElementDrawerBase { /// @@ -18,6 +23,7 @@ public override bool CanDraw(ZplElementBase element) return element is ZplFieldBlock; } + /// public override bool IsReverseDraw(ZplElementBase element) { if (element is ZplFieldBlock fieldBlock) @@ -28,12 +34,6 @@ public override bool IsReverseDraw(ZplElementBase element) return false; } - /// - public override void Draw(ZplElementBase element) - { - Draw(element, new DrawerOptions()); - } - /// public override void Draw(ZplElementBase element, DrawerOptions options) { @@ -61,7 +61,11 @@ public override void Draw(ZplElementBase element, DrawerOptions options) } var skFont = new SKFont(typeface, fontSize, scaleX); - using var skPaint = new SKPaint(skFont) { IsAntialias = true, }; + using var skPaint = new SKPaint(skFont) + { + IsAntialias = options.Antialias + }; + var textBoundBaseline = new SKRect(); skPaint.MeasureText("X", ref textBoundBaseline); @@ -79,7 +83,7 @@ public override void Draw(ZplElementBase element, DrawerOptions options) if (fieldBlock.FieldTypeset != null) { - totalHeight = lineHeight * (fieldBlock.MaxLineCount-1) + textBoundBaseline.Height; + totalHeight = lineHeight * (fieldBlock.MaxLineCount - 1) + textBoundBaseline.Height; y -= totalHeight; } @@ -155,7 +159,7 @@ public override void Draw(ZplElementBase element, DrawerOptions options) { skPaint.BlendMode = SKBlendMode.Xor; } - + this._skCanvas.DrawShapedText(textLine, x, y, skPaint); y += lineHeight; } @@ -172,7 +176,7 @@ private IEnumerable WordWrap(string text, SKFont font, int maxWidth) var words = new Stack(text.Split(new[] { ' ' }, StringSplitOptions.None).Reverse()); var line = new StringBuilder(); float width = 0; - while(words.Any()) + while (words.Any()) { var word = words.Pop(); if (word.Contains(@"\&")) diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicBoxElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicBoxElementDrawer.cs index 336aaba6..3a327c88 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicBoxElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicBoxElementDrawer.cs @@ -44,7 +44,7 @@ public override bool ForceBitmapDraw(ZplElementBase element) } /// - public override void Draw(ZplElementBase element) + public override void Draw(ZplElementBase element, DrawerOptions options) { if (element is ZplGraphicBox graphicBox) { @@ -111,11 +111,14 @@ public override void Draw(ZplElementBase element) var width = width1 - border2; var height = height1 - border2; - using var skPaint = new SKPaint(); - skPaint.Style = SKPaintStyle.Stroke; - skPaint.StrokeCap = SKStrokeCap.Square; - skPaint.Color = SKColors.Black; - skPaint.StrokeWidth = border2; + using var skPaint = new SKPaint() + { + IsAntialias = options.Antialias, + Style = SKPaintStyle.Stroke, + StrokeCap = SKStrokeCap.Square, + Color = SKColors.Black, + StrokeWidth = border2 + }; if (graphicBox.LineColor == LineColor.White) { diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicCircleElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicCircleElementDrawer.cs index eb0ce2e0..44498f44 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicCircleElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicCircleElementDrawer.cs @@ -33,7 +33,7 @@ public override bool IsWhiteDraw(ZplElementBase element) } /// - public override void Draw(ZplElementBase element) + public override void Draw(ZplElementBase element, DrawerOptions options) { if (element is ZplGraphicCircle graphicCircle) { @@ -45,10 +45,13 @@ public override void Draw(ZplElementBase element) border = radius; } - using var skPaint = new SKPaint(); - skPaint.Style = SKPaintStyle.Stroke; - skPaint.Color = SKColors.Black; - skPaint.StrokeWidth = border; + using var skPaint = new SKPaint() + { + IsAntialias = options.Antialias, + Style = SKPaintStyle.Stroke, + Color = SKColors.Black, + StrokeWidth = border + }; if (graphicCircle.LineColor == LineColor.White) { skPaint.Color = SKColors.White; diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicFieldElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicFieldElementDrawer.cs index 350ddfd6..dd98927c 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicFieldElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/GraphicFieldElementDrawer.cs @@ -1,10 +1,12 @@ using BinaryKits.Zpl.Label.Elements; using BinaryKits.Zpl.Label.Helpers; -using BinaryKits.Zpl.Viewer.Helpers; using SkiaSharp; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Graphic Field elements + /// public class GraphicFieldElementDrawer : ElementDrawerBase { /// diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/IElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/IElementDrawer.cs index 184eb7d7..cafc9e3a 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/IElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/IElementDrawer.cs @@ -3,6 +3,9 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Public interface for element drawers + /// public interface IElementDrawer { /// @@ -15,14 +18,14 @@ void Prepare( SKCanvas skCanvas); /// - /// Check the drawer can draw this element + /// Check if the drawer can draw this element /// /// /// bool CanDraw(ZplElementBase element); /// - /// Element require reverse draw + /// Element requires reverse draw /// /// /// diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/ImageMoveElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/ImageMoveElementDrawer.cs index 00eb3219..73940668 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/ImageMoveElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/ImageMoveElementDrawer.cs @@ -3,6 +3,9 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Image Move elements + /// public class ImageMoveElementDrawer : ElementDrawerBase { /// diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Interleaved2of5BarcodeDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Interleaved2of5BarcodeDrawer.cs index b0eef101..2bf9f073 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/Interleaved2of5BarcodeDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/Interleaved2of5BarcodeDrawer.cs @@ -1,12 +1,13 @@ -using BarcodeLib; using BinaryKits.Zpl.Label.Elements; -using BinaryKits.Zpl.Viewer.Helpers; using SkiaSharp; using System; -using System.Drawing; +using ZXing.OneD; namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Interleaved 2 of 5 Barcode elements + /// public class Interleaved2of5BarcodeDrawer : BarcodeDrawerBase { /// @@ -15,12 +16,6 @@ public override bool CanDraw(ZplElementBase element) return element is ZplBarcodeInterleaved2of5; } - /// - public override void Draw(ZplElementBase element) - { - Draw(element, new DrawerOptions()); - } - /// public override void Draw(ZplElementBase element, DrawerOptions options) { @@ -29,24 +24,22 @@ public override void Draw(ZplElementBase element, DrawerOptions options) float x = barcode.PositionX; float y = barcode.PositionY; - float labelFontSize = Math.Min(barcode.ModuleWidth * 7.2f, 72f); - var labelTypeFace = options.FontLoader("A"); - var labelFont = new SKFont(labelTypeFace, labelFontSize).ToSystemDrawingFont(); - int labelHeight = barcode.PrintInterpretationLine ? labelFont.Height : 0; - int labelHeightOffset = barcode.PrintInterpretationLineAboveCode ? labelHeight : 0; + var writer = new ITFWriter(); + var result = writer.encode(barcode.Content); + int narrow = barcode.ModuleWidth; + int wide = (int)Math.Floor(barcode.WideBarToNarrowBarWidthRatio * narrow); + result = this.AdjustWidths(result, wide, narrow); + using var resizedImage = this.BoolArrayToSKBitmap(result, barcode.Height); + var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation); - var barcodeElement = new Barcode + if (barcode.PrintInterpretationLine) { - BarWidth = barcode.ModuleWidth, - BackColor = Color.Transparent, - Height = barcode.Height + labelHeight, - IncludeLabel = barcode.PrintInterpretationLine, - LabelPosition = barcode.PrintInterpretationLineAboveCode ? LabelPositions.TOPCENTER : LabelPositions.BOTTOMCENTER, - LabelFont = labelFont - }; - - using var image = barcodeElement.Encode(TYPE.Interleaved2of5, barcode.Content); - this.DrawBarcode(this.GetImageData(image), barcode.Height, image.Width, barcode.FieldOrigin != null, x, y, labelHeightOffset, barcode.FieldOrientation); + float labelFontSize = Math.Min(barcode.ModuleWidth * 10f, 100f); + var labelTypeFace = options.FontLoader("A"); + var labelFont = new SKFont(labelTypeFace, labelFontSize); + this.DrawInterpretationLine(barcode.Content, labelFont, x, y, resizedImage.Width, resizedImage.Height, barcode.FieldOrigin != null, barcode.FieldOrientation, barcode.PrintInterpretationLineAboveCode, options); + } } } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/MaxiCodeElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/MaxiCodeElementDrawer.cs index bc2f934e..f3d26568 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/MaxiCodeElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/MaxiCodeElementDrawer.cs @@ -134,12 +134,11 @@ public override void Draw(ZplElementBase element) this.DrawBarcode( data, + maxiCode.PositionX, + maxiCode.PositionY, section.Width, section.Height, true, - maxiCode.PositionX, - maxiCode.PositionY, - 0, Label.FieldOrientation.Normal ); } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs index 3c2d4b30..1ecca2ec 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/PDF417ElementDrawer.cs @@ -90,7 +90,7 @@ public override void Draw(ZplElementBase element) using var resizedImage = this.BitMatrixToSKBitmap(result, 1); { var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); - this.DrawBarcode(png, resizedImage.Height, resizedImage.Width, pdf417.FieldOrigin != null, x, y, 0, pdf417.FieldOrientation); + this.DrawBarcode(png, x, y, resizedImage.Width, resizedImage.Height, pdf417.FieldOrigin != null, pdf417.FieldOrientation); } } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/QrCodeElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/QrCodeElementDrawer.cs index 38fcf52d..701555f6 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/QrCodeElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/QrCodeElementDrawer.cs @@ -8,8 +8,13 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for QR Code Barcode elements + /// public class QrCodeElementDrawer : BarcodeDrawerBase { + private static readonly Regex gs1Regex = new Regex(@"^>;>8(.+)$", RegexOptions.Compiled); + /// public override bool CanDraw(ZplElementBase element) { @@ -27,15 +32,18 @@ public override void Draw(ZplElementBase element) // support hand-rolled GS1 bool gs1Mode = false; var content = qrcode.Content; - if (Regex.Match(content, @"(^>;>8)", RegexOptions.None).Success) + + Match gs1Match = gs1Regex.Match(content); + if (gs1Match.Success) { - content = Regex.Replace(content, @"(^>;>8)", ""); + content = gs1Match.Groups[1].Value; gs1Mode = true; } int verticalQuietZone = 10; var writer = new QRCodeWriter(); + // TODO: use QrCodeEncodingOptions in next version of ZXing.NET var hints = new Dictionary { { EncodeHintType.ERROR_CORRECTION, CovertErrorCorrection(qrcode.ErrorCorrectionLevel) }, { EncodeHintType.QR_MASK_PATTERN, qrcode.MaskValue }, @@ -48,7 +56,7 @@ public override void Draw(ZplElementBase element) using var resizedImage = this.BitMatrixToSKBitmap(result, qrcode.MagnificationFactor); var png = resizedImage.Encode(SKEncodedImageFormat.Png, 100).ToArray(); - this.DrawBarcode(png, resizedImage.Height + 2 * verticalQuietZone, resizedImage.Width, qrcode.FieldOrigin != null, x, y + verticalQuietZone, 0, qrcode.FieldOrientation); + this.DrawBarcode(png, x, y + verticalQuietZone, resizedImage.Width, resizedImage.Height + 2 * verticalQuietZone, qrcode.FieldOrigin != null, qrcode.FieldOrientation); } } @@ -63,5 +71,6 @@ private ZXing.QrCode.Internal.ErrorCorrectionLevel CovertErrorCorrection(ErrorCo _ => ZXing.QrCode.Internal.ErrorCorrectionLevel.M }; } + } } diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/RecallGraphicElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/RecallGraphicElementDrawer.cs index 4ec18641..8e025e2e 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/RecallGraphicElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/RecallGraphicElementDrawer.cs @@ -3,6 +3,9 @@ namespace BinaryKits.Zpl.Viewer.ElementDrawers { + /// + /// Drawer for Recall Graphic elements + /// public class RecallGraphicElementDrawer : ElementDrawerBase { /// diff --git a/src/BinaryKits.Zpl.Viewer/ElementDrawers/TextFieldElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ElementDrawers/TextFieldElementDrawer.cs index 80e5c0fd..3659e855 100644 --- a/src/BinaryKits.Zpl.Viewer/ElementDrawers/TextFieldElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ElementDrawers/TextFieldElementDrawer.cs @@ -2,7 +2,6 @@ using BinaryKits.Zpl.Viewer.Helpers; using SkiaSharp; using SkiaSharp.HarfBuzz; -using System; namespace BinaryKits.Zpl.Viewer.ElementDrawers { @@ -24,12 +23,6 @@ public override bool IsReverseDraw(ZplElementBase element) return false; } - /// - public override void Draw(ZplElementBase element) - { - Draw(element, new DrawerOptions()); - } - /// public override void Draw(ZplElementBase element, DrawerOptions options) { @@ -50,7 +43,10 @@ public override void Draw(ZplElementBase element, DrawerOptions options) var typeface = options.FontLoader(font.FontName); var skFont = new SKFont(typeface, fontSize, scaleX); - using var skPaint = new SKPaint(skFont) { IsAntialias = true, }; + using var skPaint = new SKPaint(skFont) + { + IsAntialias = options.Antialias + }; string displayText = textField.Text; if (textField.UseHexadecimalIndicator) @@ -77,13 +73,13 @@ public override void Draw(ZplElementBase element, DrawerOptions options) switch (textField.Font.FieldOrientation) { case Label.FieldOrientation.Rotated90: - matrix = SKMatrix.CreateRotationDegrees(90, textField.PositionX + fontSize / 2, textField.PositionY + fontSize / 2); + matrix = SKMatrix.CreateRotationDegrees(90, x + fontSize / 2, y + fontSize / 2); break; case Label.FieldOrientation.Rotated180: - matrix = SKMatrix.CreateRotationDegrees(180, textField.PositionX + textBounds.Width / 2, textField.PositionY + fontSize / 2); + matrix = SKMatrix.CreateRotationDegrees(180, x + textBounds.Width / 2, y + fontSize / 2); break; case Label.FieldOrientation.Rotated270: - matrix = SKMatrix.CreateRotationDegrees(270, textField.PositionX + textBounds.Width / 2, textField.PositionY + textBounds.Width / 2); + matrix = SKMatrix.CreateRotationDegrees(270, x + textBounds.Width / 2, y + textBounds.Width / 2); break; case Label.FieldOrientation.Normal: break; @@ -94,13 +90,13 @@ public override void Draw(ZplElementBase element, DrawerOptions options) switch (textField.Font.FieldOrientation) { case Label.FieldOrientation.Rotated90: - matrix = SKMatrix.CreateRotationDegrees(90, textField.PositionX, textField.PositionY); + matrix = SKMatrix.CreateRotationDegrees(90, x, y); break; case Label.FieldOrientation.Rotated180: - matrix = SKMatrix.CreateRotationDegrees(180, textField.PositionX, textField.PositionY); + matrix = SKMatrix.CreateRotationDegrees(180, x, y); break; case Label.FieldOrientation.Rotated270: - matrix = SKMatrix.CreateRotationDegrees(270, textField.PositionX, textField.PositionY); + matrix = SKMatrix.CreateRotationDegrees(270, x, y); break; case Label.FieldOrientation.Normal: break; diff --git a/src/BinaryKits.Zpl.Viewer/Helpers/FontHelper.cs b/src/BinaryKits.Zpl.Viewer/Helpers/FontHelper.cs deleted file mode 100644 index 8ead376e..00000000 --- a/src/BinaryKits.Zpl.Viewer/Helpers/FontHelper.cs +++ /dev/null @@ -1,28 +0,0 @@ -using SkiaSharp; -using System; -using System.Drawing; -using System.Drawing.Text; -using System.Runtime.InteropServices; - -namespace BinaryKits.Zpl.Viewer.Helpers -{ - public static class FontHelper - { - - public static Font ToSystemDrawingFont(this SKFont skFont) - { - using(var fontCollection = new PrivateFontCollection()) - using(var fontStream = skFont.Typeface.OpenStream()) - { - byte[] fontBytes = (byte[])Array.CreateInstance(typeof(byte), fontStream.Length); - fontStream.Read(fontBytes, fontBytes.Length); - IntPtr fontPtr = Marshal.AllocCoTaskMem(fontBytes.Length); - Marshal.Copy(fontBytes, 0, fontPtr, fontBytes.Length); - fontCollection.AddMemoryFont(fontPtr, fontBytes.Length); - Marshal.FreeCoTaskMem(fontPtr); - return new Font(fontCollection.Families[0], skFont.Size); - } - } - - } -} diff --git a/src/BinaryKits.Zpl.Viewer/Symologies/ZplCode128Symbology.cs b/src/BinaryKits.Zpl.Viewer/Symologies/ZplCode128Symbology.cs new file mode 100644 index 00000000..9e137a9f --- /dev/null +++ b/src/BinaryKits.Zpl.Viewer/Symologies/ZplCode128Symbology.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace BinaryKits.Zpl.Viewer.Symologies +{ + public enum Code128CodeSet + { + Code128 = 0, + Code128A = 103, + Code128B = 104, + Code128C = 105 + } + + public class ZplCode128Symbology + { + // detect Type-C as late as possible + // ABC12345 -> START_B A B C 1 CODE_C 23 45 CHECK STOP + // and not -> START_B A B C CODE_C 12 34 CODE_B 5 CHECK STOP + private static readonly Regex swtichCRegex = new Regex(@"^\d\d(?:\d\d)+(?!\d)", RegexOptions.Compiled); + private static readonly Regex startCRegex = new Regex(@"^\d{4}", RegexOptions.Compiled); + private static readonly Regex digitPairRegex = new Regex(@"^\d\d", RegexOptions.Compiled); + + private static readonly Regex startCodeRegex = new Regex(@"^(>[9:;])(.+)$", RegexOptions.Compiled); + + private static readonly Dictionary invocationMap = new Dictionary() { + { "><", 62 }, + { ">0", 30 }, + { ">=", 94 }, + { ">1", 95 }, + { ">2", 96 }, + { ">3", 97 }, + { ">4", 98 }, + { ">5", 99 }, + { ">6", 100 }, + { ">7", 101 }, + { ">8", 102 } + }; + + private static readonly Dictionary startCodeMap = new Dictionary() { + { ">9", Code128CodeSet.Code128A }, + { ">:", Code128CodeSet.Code128B }, + { ">;", Code128CodeSet.Code128C } + }; + + private const string FNC_1 = "FNC_1"; + private const string FNC_2 = "FNC_2"; + private const string FNC_3 = "FNC_3"; + private const string FNC_4 = "FNC_4"; + + private const string SHIFT_A = "SHIFT_A"; + private const string SHIFT_B = "SHIFT_B"; + + private const string CODE_A = "CODE_A"; + private const string CODE_B = "CODE_B"; + private const string CODE_C = "CODE_C"; + + private const string START_A = "START_A"; + private const string START_B = "START_B"; + private const string START_C = "START_C"; + + private const string STOP = "STOP"; + + private static readonly int[] patterns; + private static readonly string[] codeAChars; + private static readonly string[] codeBChars; + private static readonly string[] codeCChars; + + private static readonly Dictionary codeAMap; + private static readonly Dictionary codeBMap; + private static readonly Dictionary codeCMap; + + private static readonly Dictionary)> codeMaps; + + /// + /// + /// + static ZplCode128Symbology() { + var codeSetTable = new[] + { + new { Value = 0, A = " ", B = " ", C = "00", Pattern = 0b11011001100 }, + new { Value = 1, A = "!", B = "!", C = "01", Pattern = 0b11001101100 }, + new { Value = 2, A = "\"", B = "\"", C = "02", Pattern = 0b11001100110 }, + new { Value = 3, A = "#", B = "#", C = "03", Pattern = 0b10010011000 }, + new { Value = 4, A = "$", B = "$", C = "04", Pattern = 0b10010001100 }, + new { Value = 5, A = "%", B = "%", C = "05", Pattern = 0b10001001100 }, + new { Value = 6, A = "&", B = "&", C = "06", Pattern = 0b10011001000 }, + new { Value = 7, A = "'", B = "'", C = "07", Pattern = 0b10011000100 }, + new { Value = 8, A = "(", B = "(", C = "08", Pattern = 0b10001100100 }, + new { Value = 9, A = ")", B = ")", C = "09", Pattern = 0b11001001000 }, + new { Value = 10, A = "*", B = "*", C = "10", Pattern = 0b11001000100 }, + new { Value = 11, A = "+", B = "+", C = "11", Pattern = 0b11000100100 }, + new { Value = 12, A = ",", B = ",", C = "12", Pattern = 0b10110011100 }, + new { Value = 13, A = "-", B = "-", C = "13", Pattern = 0b10011011100 }, + new { Value = 14, A = ".", B = ".", C = "14", Pattern = 0b10011001110 }, + new { Value = 15, A = "/", B = "/", C = "15", Pattern = 0b10111001100 }, + new { Value = 16, A = "0", B = "0", C = "16", Pattern = 0b10011101100 }, + new { Value = 17, A = "1", B = "1", C = "17", Pattern = 0b10011100110 }, + new { Value = 18, A = "2", B = "2", C = "18", Pattern = 0b11001110010 }, + new { Value = 19, A = "3", B = "3", C = "19", Pattern = 0b11001011100 }, + new { Value = 20, A = "4", B = "4", C = "20", Pattern = 0b11001001110 }, + new { Value = 21, A = "5", B = "5", C = "21", Pattern = 0b11011100100 }, + new { Value = 22, A = "6", B = "6", C = "22", Pattern = 0b11001110100 }, + new { Value = 23, A = "7", B = "7", C = "23", Pattern = 0b11101101110 }, + new { Value = 24, A = "8", B = "8", C = "24", Pattern = 0b11101001100 }, + new { Value = 25, A = "9", B = "9", C = "25", Pattern = 0b11100101100 }, + new { Value = 26, A = ":", B = ":", C = "26", Pattern = 0b11100100110 }, + new { Value = 27, A = ";", B = ";", C = "27", Pattern = 0b11101100100 }, + new { Value = 28, A = "<", B = "<", C = "28", Pattern = 0b11100110100 }, + new { Value = 29, A = "=", B = "=", C = "29", Pattern = 0b11100110010 }, + new { Value = 30, A = ">", B = ">", C = "30", Pattern = 0b11011011000 }, + new { Value = 31, A = "?", B = "?", C = "31", Pattern = 0b11011000110 }, + new { Value = 32, A = "@", B = "@", C = "32", Pattern = 0b11000110110 }, + new { Value = 33, A = "A", B = "A", C = "33", Pattern = 0b10100011000 }, + new { Value = 34, A = "B", B = "B", C = "34", Pattern = 0b10001011000 }, + new { Value = 35, A = "C", B = "C", C = "35", Pattern = 0b10001000110 }, + new { Value = 36, A = "D", B = "D", C = "36", Pattern = 0b10110001000 }, + new { Value = 37, A = "E", B = "E", C = "37", Pattern = 0b10001101000 }, + new { Value = 38, A = "F", B = "F", C = "38", Pattern = 0b10001100010 }, + new { Value = 39, A = "G", B = "G", C = "39", Pattern = 0b11010001000 }, + new { Value = 40, A = "H", B = "H", C = "40", Pattern = 0b11000101000 }, + new { Value = 41, A = "I", B = "I", C = "41", Pattern = 0b11000100010 }, + new { Value = 42, A = "J", B = "J", C = "42", Pattern = 0b10110111000 }, + new { Value = 43, A = "K", B = "K", C = "43", Pattern = 0b10110001110 }, + new { Value = 44, A = "L", B = "L", C = "44", Pattern = 0b10001101110 }, + new { Value = 45, A = "M", B = "M", C = "45", Pattern = 0b10111011000 }, + new { Value = 46, A = "N", B = "N", C = "46", Pattern = 0b10111000110 }, + new { Value = 47, A = "O", B = "O", C = "47", Pattern = 0b10001110110 }, + new { Value = 48, A = "P", B = "P", C = "48", Pattern = 0b11101110110 }, + new { Value = 49, A = "Q", B = "Q", C = "49", Pattern = 0b11010001110 }, + new { Value = 50, A = "R", B = "R", C = "50", Pattern = 0b11000101110 }, + new { Value = 51, A = "S", B = "S", C = "51", Pattern = 0b11011101000 }, + new { Value = 52, A = "T", B = "T", C = "52", Pattern = 0b11011100010 }, + new { Value = 53, A = "U", B = "U", C = "53", Pattern = 0b11011101110 }, + new { Value = 54, A = "V", B = "V", C = "54", Pattern = 0b11101011000 }, + new { Value = 55, A = "W", B = "W", C = "55", Pattern = 0b11101000110 }, + new { Value = 56, A = "X", B = "X", C = "56", Pattern = 0b11100010110 }, + new { Value = 57, A = "Y", B = "Y", C = "57", Pattern = 0b11101101000 }, + new { Value = 58, A = "Z", B = "Z", C = "58", Pattern = 0b11101100010 }, + new { Value = 59, A = "[", B = "[", C = "59", Pattern = 0b11100011010 }, + new { Value = 60, A = "\\", B = "\\", C = "60", Pattern = 0b11101111010 }, + new { Value = 61, A = "]", B = "]", C = "61", Pattern = 0b11001000010 }, + new { Value = 62, A = "^", B = "^", C = "62", Pattern = 0b11110001010 }, + new { Value = 63, A = "_", B = "_", C = "63", Pattern = 0b10100110000 }, + new { Value = 64, A = "\x00", B = "`", C = "64", Pattern = 0b10100001100 }, + new { Value = 65, A = "\x01", B = "a", C = "65", Pattern = 0b10010110000 }, + new { Value = 66, A = "\x02", B = "b", C = "66", Pattern = 0b10010000110 }, + new { Value = 67, A = "\x03", B = "c", C = "67", Pattern = 0b10000101100 }, + new { Value = 68, A = "\x04", B = "d", C = "68", Pattern = 0b10000100110 }, + new { Value = 69, A = "\x05", B = "e", C = "69", Pattern = 0b10110010000 }, + new { Value = 70, A = "\x06", B = "f", C = "70", Pattern = 0b10110000100 }, + new { Value = 71, A = "\x07", B = "g", C = "71", Pattern = 0b10011010000 }, + new { Value = 72, A = "\x08", B = "h", C = "72", Pattern = 0b10011000010 }, + new { Value = 73, A = "\x09", B = "i", C = "73", Pattern = 0b10000110100 }, + new { Value = 74, A = "\x0a", B = "j", C = "74", Pattern = 0b10000110010 }, + new { Value = 75, A = "\x0b", B = "k", C = "75", Pattern = 0b11000010010 }, + new { Value = 76, A = "\x0c", B = "l", C = "76", Pattern = 0b11001010000 }, + new { Value = 77, A = "\x0d", B = "m", C = "77", Pattern = 0b11110111010 }, + new { Value = 78, A = "\x0e", B = "n", C = "78", Pattern = 0b11000010100 }, + new { Value = 79, A = "\x0f", B = "o", C = "79", Pattern = 0b10001111010 }, + new { Value = 80, A = "\x10", B = "p", C = "80", Pattern = 0b10100111100 }, + new { Value = 81, A = "\x11", B = "q", C = "81", Pattern = 0b10010111100 }, + new { Value = 82, A = "\x12", B = "r", C = "82", Pattern = 0b10010011110 }, + new { Value = 83, A = "\x13", B = "s", C = "83", Pattern = 0b10111100100 }, + new { Value = 84, A = "\x14", B = "t", C = "84", Pattern = 0b10011110100 }, + new { Value = 85, A = "\x15", B = "u", C = "85", Pattern = 0b10011110010 }, + new { Value = 86, A = "\x16", B = "v", C = "86", Pattern = 0b11110100100 }, + new { Value = 87, A = "\x17", B = "w", C = "87", Pattern = 0b11110010100 }, + new { Value = 88, A = "\x18", B = "x", C = "88", Pattern = 0b11110010010 }, + new { Value = 89, A = "\x19", B = "y", C = "89", Pattern = 0b11011011110 }, + new { Value = 90, A = "\x1a", B = "z", C = "90", Pattern = 0b11011110110 }, + new { Value = 91, A = "\x1b", B = "{", C = "91", Pattern = 0b11110110110 }, + new { Value = 92, A = "\x1c", B = "|", C = "92", Pattern = 0b10101111000 }, + new { Value = 93, A = "\x1d", B = "}", C = "93", Pattern = 0b10100011110 }, + new { Value = 94, A = "\x1e", B = "~", C = "94", Pattern = 0b10001011110 }, + new { Value = 95, A = "\x1f", B = "\x7f", C = "95", Pattern = 0b10111101000 }, + new { Value = 96, A = FNC_3, B = FNC_3, C = "96", Pattern = 0b10111100010 }, + new { Value = 97, A = FNC_2, B = FNC_2, C = "97", Pattern = 0b11110101000 }, + new { Value = 98, A = SHIFT_B, B = SHIFT_A, C = "98", Pattern = 0b11110100010 }, + new { Value = 99, A = CODE_C, B = CODE_C, C = "99", Pattern = 0b10111011110 }, + new { Value = 100, A = CODE_B, B = FNC_4, C = CODE_B, Pattern = 0b10111101110 }, + new { Value = 101, A = FNC_4, B = CODE_A, C = CODE_A, Pattern = 0b11101011110 }, + new { Value = 102, A = FNC_1, B = FNC_1, C = FNC_1, Pattern = 0b11110101110 }, + new { Value = 103, A = START_A, B = START_A, C = START_A, Pattern = 0b11010000100 }, + new { Value = 104, A = START_B, B = START_B, C = START_B, Pattern = 0b11010010000 }, + new { Value = 105, A = START_C, B = START_C, C = START_C, Pattern = 0b11010011100 }, + new { Value = 106, A = STOP, B = STOP, C = STOP, Pattern = 0b1100011101011 }, + }; + + patterns = codeSetTable.Select(item => item.Pattern).ToArray(); + codeAChars = codeSetTable.Select(item => item.A).ToArray(); + codeBChars = codeSetTable.Select(item => item.B).ToArray(); + codeCChars = codeSetTable.Select(item => item.C).ToArray(); + + codeAMap = codeSetTable.ToDictionary(item => item.A, item => item.Value); + codeBMap = codeSetTable.ToDictionary(item => item.B, item => item.Value); + codeCMap = codeSetTable.ToDictionary(item => item.C, item => item.Value); + + codeMaps = new Dictionary)>() + { + { Code128CodeSet.Code128A, (codeAChars, codeAMap) }, + { Code128CodeSet.Code128B, (codeBChars, codeBMap) }, + { Code128CodeSet.Code128C, (codeCChars, codeCMap) }, + }; + } + + public static (List, string) Encode(string content, Code128CodeSet initialCodeSet, bool gs1) + { + List result = new List(); + var (data, interpretation) = Analyze(content, initialCodeSet); + + // TODO: magic constant FNC_1 + if (gs1 && data[1] != 102) + { + data.Insert(1, 102); + } + + foreach (int item in data) + { + result.AddRange(IntToBitArray(patterns[item])); + } + + int checksum = ComputeChecksum(data.ToArray()); + result.AddRange(IntToBitArray(patterns[checksum])); + // TODO: magic constant STOP + result.AddRange(IntToBitArray(patterns[106])); + + return (result, interpretation); + } + + private static int ComputeChecksum(int[] data) + { + int checksum = data[0]; + for (int i = 1; i < data.Length; i++) + { + checksum += i * data[i] % 103; + } + + return checksum % 103; + } + + private static IEnumerable IntToBitArray(int value) + { + Stack stack = new Stack(); + while (value > 0) + { + stack.Push((value & 1) == 1); + value >>= 1; + } + + return stack; + } + + private static (List, string) Analyze(string content, Code128CodeSet initialCodeSet) + { + List data = new(); + string interpretation = ""; + Code128CodeSet codeSet = initialCodeSet; + Match startCodeMatch = startCodeRegex.Match(content); + while (startCodeMatch.Success) + { + codeSet = startCodeMap[startCodeMatch.Groups[1].Value]; + content = startCodeMatch.Groups[2].Value; + startCodeMatch = startCodeRegex.Match(content); + } + + if (codeSet == Code128CodeSet.Code128) + { + return AnalyzeAuto(content); + } + + (var codeChars, var codeMap) = codeMaps[codeSet]; + + data.Add((int)codeSet); + for (int i = 0; i < content.Length; i++) + { + string symbol = content[i].ToString(); + if (symbol == ">" && i + 1 < content.Length) + { + i += 1; + symbol += content[i]; + if (invocationMap.ContainsKey(symbol)) + { + int value = invocationMap[symbol]; + data.Add(value); + string code = codeChars[value]; + if (code == CODE_A) + { + codeSet = Code128CodeSet.Code128A; + (codeChars, codeMap) = codeMaps[codeSet]; + } + else if (code == CODE_B) + { + codeSet = Code128CodeSet.Code128B; + (codeChars, codeMap) = codeMaps[codeSet]; + } + else if (code == CODE_C) + { + codeSet = Code128CodeSet.Code128C; + (codeChars, codeMap) = codeMaps[codeSet]; + } + } + else if (startCodeMap.ContainsKey(symbol)) + { + codeSet = startCodeMap[symbol]; + (codeChars, codeMap) = codeMaps[codeSet]; + } + else + { + throw new Exception($"Invalid invocation sequence in ZplCode128: {symbol}"); + } + } + else + { + if (codeSet == Code128CodeSet.Code128C) + { + if (i + 1 < content.Length) + { + i += 1; + symbol += content[i]; + } + else + { + codeSet = Code128CodeSet.Code128B; + (codeChars, codeMap) = codeMaps[codeSet]; + } + } + + if (!codeMap.ContainsKey(symbol)) { + throw new Exception($"Invalid symbol for {codeSet}: {symbol}"); + } + + data.Add(codeMap[symbol]); + interpretation += symbol; + } + } + + return (data, interpretation); + } + + private static (List, string) AnalyzeAuto(string content) + { + List data = new List(); + string interpretation = ""; + Code128CodeSet codeSet = Code128CodeSet.Code128B; + var codeMap = codeBMap; + if (startCRegex.IsMatch(content)) + { + codeSet = Code128CodeSet.Code128C; + codeMap = codeCMap; + } + data.Add((int)codeSet); + + while (content.Length > 0) + { + if (codeSet != Code128CodeSet.Code128C && swtichCRegex.IsMatch(content)) + { + data.Add(codeMap[CODE_C]); + codeSet = Code128CodeSet.Code128C; + codeMap = codeCMap; + } + else if (codeSet == Code128CodeSet.Code128C && !digitPairRegex.IsMatch(content)) + { + data.Add(codeMap[CODE_B]); + codeSet = Code128CodeSet.Code128B; + codeMap = codeBMap; + } + else + { + string symbol = content[0].ToString(); + if (codeSet == Code128CodeSet.Code128C) + { + symbol += content[1]; + content = content.Substring(2); + } + else + { + content = content.Substring(1); + } + + data.Add(codeMap[symbol]); + interpretation += symbol; + } + } + + return (data, interpretation); + } + + } +} diff --git a/src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs b/src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs index 0192b848..e816499d 100644 --- a/src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs +++ b/src/BinaryKits.Zpl.Viewer/ZplAnalyzer.cs @@ -12,6 +12,8 @@ namespace BinaryKits.Zpl.Viewer { public class ZplAnalyzer : IZplAnalyzer { + private static readonly Regex verticalWhitespaceRegex = new Regex(@"[\n\v\f\r]", RegexOptions.Compiled); + private readonly VirtualPrinter _virtualPrinter; private readonly IPrinterStorage _printerStorage; private readonly IFormatMerger _formatMerger; @@ -136,7 +138,7 @@ private string[] SplitZplCommands(string zplData) return Array.Empty(); } - var cleanZpl = Regex.Replace(zplData, @"\r|\n", string.Empty); + var cleanZpl = verticalWhitespaceRegex.Replace(zplData, string.Empty); char caret = '^'; char tilde = '~'; List results = new(200); diff --git a/src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs b/src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs index d1583750..a94c33b8 100644 --- a/src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs +++ b/src/BinaryKits.Zpl.Viewer/ZplElementDrawer.cs @@ -207,7 +207,7 @@ public List DrawMulti( image = surfaceWhiteBg.Snapshot(); } - var imageData = image.Encode(SKEncodedImageFormat.Png, 80); + var imageData = image.Encode(_drawerOptions.RenderFormat, _drawerOptions.RenderQuality); result.Add(imageData.ToArray()); //only return image