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

Added support for printing Aztec codes and setting left margin print position #217

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions ESCPOS_NET.ConsoleTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ static void Main(string[] args)
{ Option.LargeByteArrays, "Large Byte Arrays" },
{ Option.CashDrawerPin2, "Cash Drawer Pin2" },
{ Option.CashDrawerPin5, "Cash Drawer Pin5" },
{ Option.PrintPosition, "Print Position" },
{ Option.Exit, "Exit" }

};
Expand Down Expand Up @@ -213,6 +214,9 @@ static void Main(string[] args)
case Option.CashDrawerPin5:
printer.Write(Tests.CashDrawerOpenPin5(e));
break;
case Option.PrintPosition:
printer.Write(Tests.Position(e));
break;
default:
Console.WriteLine("Invalid entry.");
break;
Expand Down Expand Up @@ -242,6 +246,7 @@ public enum Option
LargeByteArrays,
CashDrawerPin2,
CashDrawerPin5,
PrintPosition,
Exit = 99
}

Expand Down
13 changes: 12 additions & 1 deletion ESCPOS_NET.ConsoleTest/Test2DCodes.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using ESCPOS_NET.Emitters;
using System;
using System.Text;

namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
private const string websiteString = "https://github.com/lukevp/ESC-POS-.NET/";

public static byte[][] TwoDimensionCodes(ICommandEmitter e) => new byte[][] {
e.PrintLine("PDF417:"),
e.Print2DCode(TwoDimensionCodeType.PDF417, websiteString),
Expand Down Expand Up @@ -36,7 +39,15 @@ public static partial class Tests

e.PrintLine("QRCODE MODEL 1 (LARGE):"),
e.Print2DCode(TwoDimensionCodeType.QRCODE_MODEL1, websiteString, Size2DCode.LARGE),
e.PrintLine()
e.PrintLine(),

e.PrintLine("AZTEC CODE (FULL_RANGE):"),
e.PrintAztecCode(websiteString, ModeTypeAztecCode.FULL_RANGE ),
e.PrintLine(),

e.PrintLine("AZTEC CODE (COMPACT):"),
e.PrintAztecCode(websiteString, ModeTypeAztecCode.COMPACT),
e.PrintLine(),
};
}
}
20 changes: 20 additions & 0 deletions ESCPOS_NET.ConsoleTest/TestPrintPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using ESCPOS_NET.Emitters;

namespace ESCPOS_NET.ConsoleTest
{
public static partial class Tests
{
public static byte[][] Position(ICommandEmitter e) => new byte[][] {
e.SetLeftMargin(0),
e.PrintLine("Left Margin: This is 0 left margin."),
e.SetLeftMargin(10),
e.PrintLine("Left Margin: This is 10 left margin."),
e.SetLeftMargin(20),
e.PrintLine("Left Margin: This is 20 left margin."),
e.SetLeftMargin(30),
e.PrintLine("Left Margin: This is 30 left margin."),
e.SetLeftMargin(0),
e.PrintLine("Left Margin: This is 0 left margin."),
};
}
}
31 changes: 31 additions & 0 deletions ESCPOS_NET/Emitters/BaseCommandEmitter/BarcodeCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace ESCPOS_NET.Emitters
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
/* Barcode Commands */

public virtual byte[] PrintBarcode(BarcodeType type, string barcode, BarcodeCode code = BarcodeCode.CODE_B)
{
DataValidator.ValidateBarcode(type, code, barcode);
Expand Down Expand Up @@ -111,5 +112,35 @@ protected virtual byte[] TwoDimensionCodeBytes(TwoDimensionCodeType type, string
public virtual byte[] SetBarLabelPosition(BarLabelPrintPosition position) => new byte[] { Cmd.GS, Barcodes.SetBarLabelPosition, (byte)position };

public virtual byte[] SetBarLabelFontB(bool fontB) => new byte[] { Cmd.GS, Barcodes.SetBarLabelFont, (byte)(fontB ? 1 : 0) };

/// <inheritdoc />
public virtual byte[] PrintAztecCode(string data, ModeTypeAztecCode modeType = ModeTypeAztecCode.FULL_RANGE, int size = 3, int correctionLevel = 23, int numberOfDataLayers = 0)
{
var bytes = data.ToCharArray().Select(x => (byte)x).ToArray();
return PrintAztecCode(bytes, modeType, size, correctionLevel, numberOfDataLayers);
}

/// <inheritdoc />
public virtual byte[] PrintAztecCode(byte[] data, ModeTypeAztecCode modeType = ModeTypeAztecCode.FULL_RANGE, int size = 3, int correctionLevel = 23, int numberOfDataLayers = 0)
{
List<byte> command = new List<byte>();
Copy link
Collaborator

@igorocampos igorocampos Sep 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any validations we could do for this command? Perhaps regarding the size of data, or min/max on correction level and layers?

Take a look at DataValidator class and see if you can implement some similar guardrails for this command.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igorocampos Hi, I finally had some time to look at this again.
I've added validation of the data size by using ZXing.Net.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you added a new dependency to the project in order to validate the data size, is there other way of validating it? I don't think we want to add external dependencies like that in the project. The empty catch is not helping much either, you are just re-throwing the very same without any friendly details to the library user, in that case, why have the try at all? As a library user, if I provide an invalid size for the data, I would like to receive a message saying at least:
1 - The data I used was not in an acceptable size
2 - What is the acceptable size

Similar to what you have done to Module Size, I guess. Other than that I would just keep the empty new lines in the end of the files, but I can open new suggestion regarding that in the review, so you can solve them as easy as a button click :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

@jwikberg jwikberg Jan 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@igorocampos If I'm not completely mistaken, the problem with validating the data length for an Aztec is that you need to start encoding it to see if it's too big when it's close to the maximum size.
It's not as simple as validating the length of a string or the number of bytes that translates to before encoding it.

I've tried to confirm this by generating random strings that are 3067 letters long, which is the maximum of an Aztec, and that will fail to print if I mix upper and lowercase letters but succeed if the string is either only upper or lowercase.
Encoding the strings with ZXing.Net matches the printer's behavior and throws an exception for the string that can't be printed.

So either I'd have to implement the entire logic for encoding the data or use a lib for it, unless there is something I'm missing of course.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, perhaps that one validation should not be implemented. I think the "price" is too high. @lukevp do you agree?

byte[] initial = { Cmd.GS, Barcodes.Set2DCode, Barcodes.PrintBarcode };

command.AddRange(initial, Barcodes.SetAztecCodeModeTypeAndNumberOfDataLayers, (byte)modeType, (byte)numberOfDataLayers);
command.AddRange(initial, Barcodes.SetAztecCodeSizeOfModule, (byte)size);
command.AddRange(initial, Barcodes.SetAztecCodeErrorCorrectionLevel, (byte)correctionLevel);

int num = data.Length + 3;
int pH = num / 256;
int pL = num % 256;

command.AddRange(initial, (byte)pL, (byte)pH, Barcodes.StoreAztecCodeData);
command.AddRange(data);

// Prints stored Aztec code
command.AddRange(initial, Barcodes.PrintAztecCode);

return command.ToArray();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using ESCPOS_NET.Emitters.BaseCommandValues;

namespace ESCPOS_NET.Emitters
{
public abstract partial class BaseCommandEmitter : ICommandEmitter
{
public virtual byte[] SetLeftMargin(int leftMargin) => new byte[] { Cmd.GS, PrintPosition.LeftMargin, (byte)leftMargin, 0x0 };
}
}
6 changes: 6 additions & 0 deletions ESCPOS_NET/Emitters/BaseCommandValues/Barcodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ public static class Barcodes
public static readonly byte[] SetQRCodeCorrectionLevel = { 0x03, 0x00, 0x31, 0x45 };
public static readonly byte[] StoreQRCodeData = { 0x31, 0x50, 0x30 };
public static readonly byte[] PrintQRCode = { 0x03, 0x00, 0x31, 0x51, 0x30 };

public static readonly byte[] SetAztecCodeModeTypeAndNumberOfDataLayers = { 0x04, 0x00, 0x35, 0x42 };
public static readonly byte[] SetAztecCodeSizeOfModule = { 0x03, 0x00, 0x35, 0x43 };
public static readonly byte[] SetAztecCodeErrorCorrectionLevel = { 0x03, 0x00, 0x35, 0x45 };
public static readonly byte[] StoreAztecCodeData = { 0x35, 0x50, 0x30 };
public static readonly byte[] PrintAztecCode = { 0x03, 0x00, 0x35, 0x51, 0x30 };
}
}
7 changes: 7 additions & 0 deletions ESCPOS_NET/Emitters/BaseCommandValues/PrintPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace ESCPOS_NET.Emitters.BaseCommandValues
{
public static class PrintPosition
{
public static readonly byte LeftMargin = 0x4C;
}
}
7 changes: 7 additions & 0 deletions ESCPOS_NET/Emitters/Enums/2DCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ public enum CorrectionLevel2DCode
PERCENT_25 = 50,
PERCENT_30 = 51,
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1025:Code should not contain multiple whitespace in a row", Justification = "Enums are easier to read if they have whitespace alignment.")]
public enum ModeTypeAztecCode
{
FULL_RANGE = 48,
COMPACT = 49,
}
}
56 changes: 53 additions & 3 deletions ESCPOS_NET/Emitters/ICommandEmitter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using ESCPOS_NET.Emitters.BaseCommandValues;

namespace ESCPOS_NET.Emitters
{
public interface ICommandEmitter
Expand Down Expand Up @@ -86,7 +84,6 @@ public interface ICommandEmitter

byte[] RequestInkStatus();


/* Barcode Commands */
byte[] PrintBarcode(BarcodeType type, string barcode, BarcodeCode code = BarcodeCode.CODE_B);

Expand All @@ -102,5 +99,58 @@ public interface ICommandEmitter
byte[] SetBarLabelPosition(BarLabelPrintPosition position);

byte[] SetBarLabelFontB(bool fontB);

/// <summary>
/// Print Aztec Code
/// </summary>
/// <param name="data">
/// <para>Data to print as Aztec Code.</para>
/// </param>
/// <param name="modeType">
/// <para>The mode type for Aztec Code.</para>
/// <para>Default is FULL_RANGE.</para>
/// </param>
/// <param name="size">
/// <para>The size of one module of Aztec Code in dot units, valid range is 2-16.</para>
/// <para>Default is 3.</para>
/// </param>
/// <param name="correctionLevel">
/// <para>The error correction level in percent, valid range is 5-95.</para>
/// <para>Default is 23.</para>
/// </param>
/// <param name="numberOfDataLayers">
/// <para>The number of data layers for Aztec Code.</para>
/// <para>0 = automatic processing for the number of layers, valid range is 0-32.</para>
/// <para>Default is 0.</para>
/// </param>
byte[] PrintAztecCode(string data, ModeTypeAztecCode modeType = ModeTypeAztecCode.FULL_RANGE, int size = 3, int correctionLevel = 23, int numberOfDataLayers = 0);

/// <summary>
/// Print Aztec Code
/// </summary>
/// <param name="data">
/// <para>Data to print as Aztec Code.</para>
/// </param>
/// <param name="modeType">
/// <para>The mode type for Aztec Code.</para>
/// <para>Default is FULL_RANGE.</para>
/// </param>
/// <param name="size">
/// <para>The size of one module of Aztec Code in dot units, valid range is 2-16.</para>
/// <para>Default is 3.</para>
/// </param>
/// <param name="correctionLevel">
/// <para>The error correction level in percent, valid range is 5-95.</para>
/// <para>Default is 23.</para>
/// </param>
/// <param name="numberOfDataLayers">
/// <para>The number of data layers for Aztec Code.</para>
/// <para>0 = automatic processing for the number of layers, valid range is 0-32.</para>
/// <para>Default is 0.</para>
/// </param>
byte[] PrintAztecCode(byte[] data, ModeTypeAztecCode modeType = ModeTypeAztecCode.FULL_RANGE, int size = 3, int correctionLevel = 23, int numberOfDataLayers = 0);

/* Print Position Commands */
byte[] SetLeftMargin(int leftMargin);
}
}