Initial commit
Initial commit til Git. V2 er deployed
This commit is contained in:
21
PointOfSale/Pos.EpsonPrinter/EncodingHelper.cs
Normal file
21
PointOfSale/Pos.EpsonPrinter/EncodingHelper.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Text;
|
||||
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public static class EncodingHelper
|
||||
{
|
||||
static EncodingHelper()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
}
|
||||
|
||||
public static Encoding GetEncodingForCodePage(PrinterCodePage codePage) =>
|
||||
codePage switch
|
||||
{
|
||||
PrinterCodePage.PC865_Nordic => Encoding.GetEncoding(865),
|
||||
PrinterCodePage.PC850_Multilingual => Encoding.GetEncoding(850),
|
||||
PrinterCodePage.PC858_Euro => Encoding.GetEncoding(858),
|
||||
PrinterCodePage.WPC1252 => Encoding.GetEncoding(1252),
|
||||
_ => Encoding.GetEncoding(865)
|
||||
};
|
||||
}
|
||||
42
PointOfSale/Pos.EpsonPrinter/EpsonNetworkReceiptPrinter.cs
Normal file
42
PointOfSale/Pos.EpsonPrinter/EpsonNetworkReceiptPrinter.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public sealed class EpsonNetworkReceiptPrinter : IReceiptPrinter
|
||||
{
|
||||
private readonly string _host;
|
||||
private readonly int _port;
|
||||
private readonly PrinterCodePage _codePage;
|
||||
|
||||
public EpsonNetworkReceiptPrinter(string host, int port = 9100, PrinterCodePage codePage = PrinterCodePage.PC865_Nordic)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(host))
|
||||
{
|
||||
throw new ArgumentException("Host must be provided.", nameof(host));
|
||||
}
|
||||
|
||||
_host = host;
|
||||
_port = port;
|
||||
_codePage = codePage;
|
||||
}
|
||||
|
||||
public async Task PrintAsync(Receipt receipt, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(receipt);
|
||||
|
||||
var data = ReceiptFormatter.Format(receipt, _codePage);
|
||||
await PrintRawAsync(data, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task PrintRawAsync(byte[] data, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(data);
|
||||
|
||||
using var client = new TcpClient();
|
||||
await client.ConnectAsync(_host, _port, cancellationToken);
|
||||
|
||||
await using var stream = client.GetStream();
|
||||
await stream.WriteAsync(data, cancellationToken);
|
||||
await stream.FlushAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
10
PointOfSale/Pos.EpsonPrinter/EpsonReceiptPrinter.csproj
Normal file
10
PointOfSale/Pos.EpsonPrinter/EpsonReceiptPrinter.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
147
PointOfSale/Pos.EpsonPrinter/EscPosBuilder.cs
Normal file
147
PointOfSale/Pos.EpsonPrinter/EscPosBuilder.cs
Normal file
@@ -0,0 +1,147 @@
|
||||
using System.Text;
|
||||
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public sealed class EscPosBuilder
|
||||
{
|
||||
private readonly List<byte> _buffer = new();
|
||||
private readonly Encoding _encoding;
|
||||
private PrinterCodePage _codePage;
|
||||
|
||||
public EscPosBuilder(PrinterCodePage codePage = PrinterCodePage.PC865_Nordic, Encoding? encoding = null)
|
||||
{
|
||||
_codePage = codePage;
|
||||
_encoding = encoding ?? EncodingHelper.GetEncodingForCodePage(codePage);
|
||||
}
|
||||
|
||||
public EscPosBuilder Initialize()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.Initialize);
|
||||
_buffer.AddRange(EscPosCommands.SelectCodePage(_codePage));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder SelectCodePage(PrinterCodePage codePage)
|
||||
{
|
||||
_codePage = codePage;
|
||||
_buffer.AddRange(EscPosCommands.SelectCodePage(codePage));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder AlignLeft()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.AlignLeft);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder AlignCenter()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.AlignCenter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder AlignRight()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.AlignRight);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder BoldOn()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.BoldOn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder BoldOff()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.BoldOff);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder UnderlineOn()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.UnderlineOn);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder UnderlineOff()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.UnderlineOff);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder SetTextSize(TextSize size)
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.TextSize(size));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder SetTextSize(int widthMultiplier, int heightMultiplier)
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.TextSize(widthMultiplier, heightMultiplier));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder Write(string text)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
_buffer.AddRange(_encoding.GetBytes(text));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder WriteLine(string? text = null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
_buffer.AddRange(_encoding.GetBytes(text));
|
||||
}
|
||||
|
||||
_buffer.Add(0x0A);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder Feed(int lines = 1)
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.Feed(lines));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder Cut(bool partial = false)
|
||||
{
|
||||
_buffer.AddRange(partial ? EscPosCommands.CutPartial() : EscPosCommands.CutFull());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder OpenCashDrawer()
|
||||
{
|
||||
_buffer.AddRange(EscPosCommands.OpenCashDrawer());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EscPosBuilder Separator(int width = 42, char character = '-')
|
||||
{
|
||||
return WriteLine(new string(character, width));
|
||||
}
|
||||
|
||||
public EscPosBuilder WriteColumns(string left, string? right, int totalWidth = 42)
|
||||
{
|
||||
left ??= string.Empty;
|
||||
right ??= string.Empty;
|
||||
|
||||
if (left.Length + right.Length >= totalWidth)
|
||||
{
|
||||
WriteLine(left);
|
||||
WriteLine(right.PadLeft(totalWidth));
|
||||
return this;
|
||||
}
|
||||
|
||||
var spaces = totalWidth - left.Length - right.Length;
|
||||
WriteLine(left + new string(' ', spaces) + right);
|
||||
return this;
|
||||
}
|
||||
|
||||
public byte[] Build() => _buffer.ToArray();
|
||||
}
|
||||
63
PointOfSale/Pos.EpsonPrinter/EscPosCommands.cs
Normal file
63
PointOfSale/Pos.EpsonPrinter/EscPosCommands.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public static class EscPosCommands
|
||||
{
|
||||
public static readonly byte[] Initialize = [0x1B, 0x40];
|
||||
|
||||
public static readonly byte[] BoldOn = [0x1B, 0x45, 0x01];
|
||||
public static readonly byte[] BoldOff = [0x1B, 0x45, 0x00];
|
||||
|
||||
public static readonly byte[] UnderlineOn = [0x1B, 0x2D, 0x01];
|
||||
public static readonly byte[] UnderlineOff = [0x1B, 0x2D, 0x00];
|
||||
|
||||
public static readonly byte[] AlignLeft = [0x1B, 0x61, 0x00];
|
||||
public static readonly byte[] AlignCenter = [0x1B, 0x61, 0x01];
|
||||
public static readonly byte[] AlignRight = [0x1B, 0x61, 0x02];
|
||||
|
||||
public static readonly byte[] FontA = [0x1B, 0x4D, 0x00];
|
||||
public static readonly byte[] FontB = [0x1B, 0x4D, 0x01];
|
||||
|
||||
public static byte[] Feed(int lines)
|
||||
{
|
||||
if (lines < 0 || lines > 255)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(lines));
|
||||
}
|
||||
|
||||
return [0x1B, 0x64, (byte)lines];
|
||||
}
|
||||
|
||||
public static byte[] CutFull() => [0x1D, 0x56, 0x00];
|
||||
|
||||
public static byte[] CutPartial() => [0x1D, 0x56, 0x01];
|
||||
|
||||
public static byte[] OpenCashDrawer() => [0x1B, 0x70, 0x00, 0x19, 0xFA];
|
||||
|
||||
public static byte[] SelectCodePage(PrinterCodePage codePage) => [0x1B, 0x74, (byte)codePage];
|
||||
|
||||
public static byte[] TextSize(TextSize size) =>
|
||||
size switch
|
||||
{
|
||||
TextSize.Normal => [0x1D, 0x21, 0x00],
|
||||
TextSize.DoubleWidth => [0x1D, 0x21, 0x10],
|
||||
TextSize.DoubleHeight => [0x1D, 0x21, 0x01],
|
||||
TextSize.DoubleWidthAndHeight => [0x1D, 0x21, 0x11],
|
||||
_ => [0x1D, 0x21, 0x00]
|
||||
};
|
||||
|
||||
public static byte[] TextSize(int widthMultiplier, int heightMultiplier)
|
||||
{
|
||||
if (widthMultiplier < 1 || widthMultiplier > 8)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(widthMultiplier));
|
||||
}
|
||||
|
||||
if (heightMultiplier < 1 || heightMultiplier > 8)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(heightMultiplier));
|
||||
}
|
||||
|
||||
var value = (byte)(((widthMultiplier - 1) << 4) | (heightMultiplier - 1));
|
||||
return [0x1D, 0x21, value];
|
||||
}
|
||||
}
|
||||
7
PointOfSale/Pos.EpsonPrinter/IReceiptPrinter.cs
Normal file
7
PointOfSale/Pos.EpsonPrinter/IReceiptPrinter.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public interface IReceiptPrinter
|
||||
{
|
||||
Task PrintAsync(Receipt receipt, CancellationToken cancellationToken = default);
|
||||
Task PrintRawAsync(byte[] data, CancellationToken cancellationToken = default);
|
||||
}
|
||||
13
PointOfSale/Pos.EpsonPrinter/PrinterCodePage.cs
Normal file
13
PointOfSale/Pos.EpsonPrinter/PrinterCodePage.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public enum PrinterCodePage
|
||||
{
|
||||
PC437_USA = 0,
|
||||
Katakana = 1,
|
||||
PC850_Multilingual = 2,
|
||||
PC860_Portuguese = 3,
|
||||
PC863_CanadianFrench = 4,
|
||||
PC865_Nordic = 5,
|
||||
WPC1252 = 16,
|
||||
PC858_Euro = 19
|
||||
}
|
||||
31
PointOfSale/Pos.EpsonPrinter/Receipt.cs
Normal file
31
PointOfSale/Pos.EpsonPrinter/Receipt.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public sealed class Receipt
|
||||
{
|
||||
public string? Header { get; set; }
|
||||
public string? SubHeader { get; set; }
|
||||
public List<ReceiptLine> Lines { get; } = new();
|
||||
public string? Footer { get; set; }
|
||||
public bool CutPaper { get; set; } = true;
|
||||
public int PaperWidthCharacters { get; set; } = 42;
|
||||
}
|
||||
|
||||
public sealed class ReceiptLine
|
||||
{
|
||||
public ReceiptLine()
|
||||
{
|
||||
}
|
||||
|
||||
public ReceiptLine(string left, string? right = null, bool emphasize = false, TextSize textSize = TextSize.Normal)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
Emphasize = emphasize;
|
||||
TextSize = textSize;
|
||||
}
|
||||
|
||||
public string Left { get; set; } = string.Empty;
|
||||
public string? Right { get; set; }
|
||||
public bool Emphasize { get; set; }
|
||||
public TextSize TextSize { get; set; } = TextSize.Normal;
|
||||
}
|
||||
72
PointOfSale/Pos.EpsonPrinter/ReceiptFormatter.cs
Normal file
72
PointOfSale/Pos.EpsonPrinter/ReceiptFormatter.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public static class ReceiptFormatter
|
||||
{
|
||||
public static byte[] Format(Receipt receipt, PrinterCodePage codePage = PrinterCodePage.PC865_Nordic)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(receipt);
|
||||
|
||||
var width = receipt.PaperWidthCharacters <= 0 ? 42 : receipt.PaperWidthCharacters;
|
||||
|
||||
var builder = new EscPosBuilder(codePage)
|
||||
.Initialize()
|
||||
.AlignCenter();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(receipt.Header))
|
||||
{
|
||||
builder
|
||||
.SetTextSize(TextSize.DoubleWidthAndHeight)
|
||||
.BoldOn()
|
||||
.WriteLine(receipt.Header)
|
||||
.BoldOff()
|
||||
.SetTextSize(TextSize.Normal);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(receipt.SubHeader))
|
||||
{
|
||||
builder.WriteLine(receipt.SubHeader);
|
||||
}
|
||||
|
||||
builder
|
||||
.Feed(1)
|
||||
.AlignLeft()
|
||||
.Separator(width);
|
||||
|
||||
foreach (var line in receipt.Lines)
|
||||
{
|
||||
builder.SetTextSize(line.TextSize);
|
||||
|
||||
if (line.Emphasize)
|
||||
{
|
||||
builder.BoldOn();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.BoldOff();
|
||||
}
|
||||
|
||||
builder.WriteColumns(line.Left, line.Right, width);
|
||||
builder.SetTextSize(TextSize.Normal);
|
||||
builder.BoldOff();
|
||||
}
|
||||
|
||||
builder
|
||||
.Separator(width)
|
||||
.Feed(1)
|
||||
.AlignCenter();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(receipt.Footer))
|
||||
{
|
||||
builder.WriteLine(receipt.Footer);
|
||||
}
|
||||
|
||||
builder.Feed(3);
|
||||
|
||||
if (receipt.CutPaper)
|
||||
{
|
||||
builder.Cut();
|
||||
}
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
9
PointOfSale/Pos.EpsonPrinter/TextSize.cs
Normal file
9
PointOfSale/Pos.EpsonPrinter/TextSize.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace EpsonReceiptPrinter;
|
||||
|
||||
public enum TextSize
|
||||
{
|
||||
Normal,
|
||||
DoubleWidth,
|
||||
DoubleHeight,
|
||||
DoubleWidthAndHeight
|
||||
}
|
||||
Reference in New Issue
Block a user